template <typename T>
class A {
public:
template<class C> A(const A<C>&) {}
A(A&&) {}
};
void f(A<int>& a)
{
A<int> b(a);
}
以上代码无法在g ++ / clang ++中编译报告复制构造函数因用户提供的移动构造函数而被删除(尽管vc ++编译正常)。是否有任何标准要求阻止在重载解析期间选择模板构造函数(我知道它不是复制构造函数)?或者是否要求初始化程序与initializee具有相同的类型时,必须选择复制构造函数?
答案 0 :(得分:4)
注意; gcc
和clang
在尝试编译提供的代码段时显示正确的行为。
vc++
错误接受该片段?函数模板永远不会以产生复制构造函数的方式实例化,因为可以在c ++标准的以下两个引号中读取:
[<强> class.copy 强>
2)类X的非模板构造函数是复制构造函数,如果其第一个参数的类型为
X&
,{{1 }},const X&
或volatile X&
,并且没有其他参数,或者所有其他参数都有默认参数(8.3.6)。...
6)类
const volatile X&
的构造函数声明如果其第一个参数是类型(可选择cv-qualified)X
并且没有其他参数或其他所有其他参数,则格式错误参数有默认参数。 永远不会实例化成员函数模板以生成此类构造函数签名。
如果显式声明 move-constructor ,则隐式生成的 copy-constructor 将不可调用,请参阅以下内容:
[<强> class.copy 强>
7)如果类定义没有显式声明复制构造函数,则会声明隐式。 如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为已删除;否则,它被定义为默认(8.4)
X
考虑到前面提到的c ++标准的引用,我们可以很容易地看到struct Obj {
template<typename T>
Obj (T const&) { }
Obj (Obj&&) { }
};
...
Obj a;
Obj b (a);
的上述定义在语义上等同于下面,因为我们显式声明的 move-constructor 将使我们隐式声明的复制构造函数Obj
。
= delete;
根据重载决议的规则,删除的功能在尝试找到最佳匹配时仍然参与,但如果他们赢得了最佳匹配的战斗,则该程序是不正确的。这正是代码片段中发生的事情。
struct Obj {
template<typename T>
Obj (T const&) { }
Obj (Obj const&) = delete;
Obj (Obj&&) { }
};
比Obj (Obj const&) = delete
更合适,但由于复制构造函数不可调用,因此代码段无法编译。