2

可能我的理解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>,请参见此处)?

4

2 回答 2

6

A中,复制构造函数没有隐藏。编译器像往常一样隐式声明它。它只是失去了重载决议,因为它的参数类型 ( const A&) 与构造函数模板 ( ) 的特化参数相比具有额外的 cv 限定A&。如果你愿意

auto b = static_cast<const A&>(a);

你会看到复制构造函数会被调用。

A_explicit中,模板根本不会作为重载决议的候选提交,因为它是声明的explicit。隐式声明的复制构造函数仍然存在,就像它在 中一样A,所以它被调用了。

于 2015-05-08T07:20:57.680 回答
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
于 2015-05-08T07:46:17.583 回答