以下程序的输出......
#include <iostream>
using namespace std;
struct X
{
X(const X&) { cout << "copy" << endl; }
X(X&&) { cout << "move" << endl; }
template<class T> X(T&&) { cout << "tmpl" << endl; }
};
int main()
{
X x1 = 42;
X x2(x1);
}
是
tmpl
tmpl
所需的输出是:
tmpl
copy
为什么具体的复制构造函数不优先于模板构造函数?
无论如何都要修复它,以便复制和移动构造函数重载优先于模板构造函数?
答案 0 :(得分:5)
嗯,这是因为reference-collapsing 。
在重载解析阶段,当实例化函数模板时,T
被推导为X&
,因此T&&
(X& &&
)变为{{1}由于引用 - 折叠,函数模板中的实例化函数变为完全匹配,复制构造函数需要从X&
到{{}的转换1}}(这就是选择劣等匹配的原因。)
但是,如果从复制构造函数中删除X&
,则首选复制构造函数。试试这个:
const X&
Output正如所料。
或者,如果你将函数模板中的参数设为const
,那么将调用copy-constructor(即使它保持不变!),因为引用折叠现在不会出现:
X(/*const*/ X&) { cout << "copy" << endl; }
预计会再次
答案 1 :(得分:3)
如果您不想添加其他构造函数(如建议的其他答案),您可以使用SFINAE来约束调用,方法是将模板构造函数替换为:
template<class T
, typename std::enable_if<not std::is_same<X, typename std::decay<T>::type>::value, int>::type = 0
> X(T&&) { cout << "tmpl " << endl; }
仅涉及添加dummy
默认模板参数(已知技术:http://www.boost.org/doc/libs/1_52_0/libs/utility/enable_if.html)。不需要额外的标题。
您将获得所需的输出。
我从相关问题得到了这个答案:http://flamingdangerzone.com/cxx11/2012/06/05/is_related.html。所有这些看起来都不那么优雅,但似乎是现在唯一的出路。我仍然希望看到更优雅的解决方案。
答案 2 :(得分:2)
正常的重载决策规则在选择构造函数时仍然适用 - 并且采用非const左值引用的构造函数(对于参数推导后的模板构造函数)是一个比采用const左值引用的构造函数更好的匹配。
当然,您可以添加另一个带有非常量左值引用的重载,即
X(X&) { cout << "copy" << endl; }
更新:模板构造函数更匹配的其他情况:
const X f()
{ return X(); }
struct Y : X
{ Y() { } };
int main()
{
X x3(f()); // const-qualified rvalue
Y y;
X x4(y); // derived class
}