可能我对
explicit
的理解还不够,但我想知道为什么在下面的代码中,当我将通用引用构造函数声明为explicit
时,复制构造函数没有被通用引用构造函数隐藏。
struct A
{
A() = default;
template<typename T>
A(T&& t) { std::cout<<"hides copy constructor"<<std::endl; }
};
struct A_explicit
{
A_explicit() = default;
template<typename T>
explicit A_explicit(T&& t) { std::cout<<"does not hide copy constructor?"<<std::endl; }
};
int main()
{
A a;
auto b = a; (void) b; //prints "hides copy constructor"
A_explicit a_exp;
auto b_exp = a_exp; (void) b_exp; //prints nothing
}
这是一种通用解决方案,而不是 SFINAE 的解决方案,否则可以应用它来防止隐藏在
A
中(例如通过 std::enable_if_t<!std::is_same<std::decay_t<T>, A>::value>
,请参阅这里)?
在
A
中,复制构造函数未隐藏。编译器像往常一样隐式声明它。它只是失去了重载解析,因为与构造函数模板特化的参数(const A&
)相比,它的参数类型(A&
)具有额外的cv限定。如果你愿意的话
auto b = static_cast<const A&>(a);
您会看到复制构造函数将被调用。
在
A_explicit
中,模板根本不会作为候选者提交到重载解析,因为它被声明为 explicit
。隐式声明的复制构造函数仍然存在,就像在A
中一样,因此它被调用。
标记为
explicit
的构造函数在复制初始化期间不参与重载解析(A a = b;
等)。
它确实参与复制列表初始化(
A a = {b1};
),并且如果选择它会导致程序格式错误。
...除非大括号内的内容是
A
或从中派生的类,在这种情况下,最近的缺陷报告更改了规则,表示在这种特殊情况下会执行复制初始化 - 所以 explicit
构造函数再次被完全忽略(demo)。
非常有教养,我知道。
这是一种通用解决方案,而不是 SFINAE 的解决方案吗? 否则是为了防止隐藏在A中?
不。因为该构造函数仍然会赢得直接初始化的重载解析:
A_explicit a, b(a); // will call the constructor taking a forwarding reference