6

我使用 g++ 6.3.0 编译了下面的代码,并带有 -std=c++14 选项。

#include <utility>
#include <iostream>
struct A{
    int x;
    A(const A&)=default;
    A(int x):x(x){}
};

struct B{
  A a;
  template<class... Args>
  B(Args&&... args):a(std::forward<Args>(args)...){
    std::cout<<"1!"<<std::endl;
  }

  explicit B(const A& a):a(a){std::cout<<"2!"<<std::endl;}
};
struct C:B{
  using B::B;
};
int main(){
    A a{2};
    const A& aref=a;
    C c=aref; //Implicit conversion
}

我希望它输出“1!” 因为转换是隐式的,但它输出“2!”。如果我注释掉模板构造函数,它将无法编译。这是正确的行为,还是某种 g++ 错误?

4

1 回答 1

1

是的,您的程序将打印1!.

显示编译器分歧的程序的简化版本是

struct B {
    int b;
    constexpr B(auto) { b = 1; }
    constexpr explicit B(int) { b = 2; }
};

struct C : B {
    using B::B;
};

constexpr B b = 0;
static_assert( b.b == 1 ); //ok everywhere

constexpr C c = 0;
static_assert( c.b == 1 ); //ok in Clang only

演示:https ://gcc.godbolt.org/z/hva4f5qs5

正如相关的 GCC 错误https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85251中所引用的那样:[namespace.udecl] p13 确实指定了这里应该发生的事情:

在查找派生类的构造函数 ([class.qual]) 或形成一组重载候选对象 ([over.match.ctor ]、[over.match.copy]、[over.match.list])。

所以explicit构造函数 fromB应该保留explicitCafterusing B::B;中,此时只有 Clang 正确处理上述程序。

更容易看出其他编译器错误的方法是删除B(auto)构造函数:

struct B {
    int b;
    constexpr explicit B(int) { b = 2; }
};

struct C : B {
    using B::B;
};

constexpr C c = 0; // error everywhere

现在所有编译器都正确拒绝了代码(演示:https ://gcc.godbolt.org/z/P3zqxMvvn ),即使它们没有在前面的示例中调用已删除的构造函数。

于 2022-02-05T17:18:44.473 回答