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