我对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>
,见here)?
答案 0 :(得分:6)
在A
中,复制构造函数未隐藏。编译器会像往常一样隐式声明它。它只是失去重载决策,因为它的参数类型(const A&
)与构造函数模板(A&
)的特化参数相比具有额外的cv限定。如果你愿意的话
auto b = static_cast<const A&>(a);
您会看到将调用复制构造函数。
在A_explicit
中,模板根本不作为重载决策的候选者提交,因为它被声明为explicit
。隐式声明的复制构造函数仍然存在,就像在A
中一样,因此它被调用。
答案 1 :(得分:5)
标记为explicit
的构造函数在复制初始化期间(A a = b;
除外)不参与重载解析。
它确实参与了复制列表初始化(A a = {b1};
),并导致程序在选中时格式不正确。
...除非大括号内的东西是A
或从中派生的类,在这种情况下,最近的缺陷报告改变了规则,说在这种特殊情况下,执行复制初始化 - 所以explicit
构造函数再一次被完全忽略。
非常可教,我知道。
这是一个通用的解决方案而不是SFINAE的东西 否则要防止隐藏在A?
没有。因为该构造函数仍然会为直接初始化赢得重载决策:
A_explicit a, b(a); // will call the constructor taking a forwarding reference