C++ 模板构造函数优先于普通复制和移动构造函数?

C++ 模板构造函数优先于普通复制和移动构造函数?,c++,c++11,C++,C++11,以下程序的输出 #include <iostream> using namespace std; struct X { X(const X&) { cout << "copy" << endl; } X(X&&) { cout << "move" << endl; } template<class T> X(T

以下程序的输出

#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
copy
为什么具体副本构造函数不优先于模板构造函数


有没有办法修复它,使复制和移动构造函数重载优先于模板构造函数?

好吧,这是因为

在重载解析阶段,当函数模板实例化时,
T
被推断为
X&
,因此
T&&
(即
X&&
)由于引用折叠而变为
X&
,来自函数模板的实例化函数变得完全匹配,复制构造函数需要从
X&
转换为
const X&
(这就是为什么它没有被选择为次匹配)

但是,如果从复制构造函数中删除
常量
,则首选复制构造函数。试试这个:

X(/*const*/ X&) { cout << "copy" << endl; }

也是预期的。

在选择构造函数时,正常的重载解析规则仍然适用-并且采用非常量左值引用的构造函数(对于参数推导后的模板构造函数)比采用常量左值引用的构造函数更匹配

当然,您可以添加另一个重载,使用非常量左值引用,即

X(X&)              { cout << "copy" << endl; }

如果不想添加另一个构造函数(如其他答案所建议的),可以使用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; }

template X(T&&){我是否还需要添加
X(const X&&)
或者这是不可能的?在没有模板构造函数的情况下,是否存在移动或复制构造函数会匹配的其他情况?@andreTomazosfathomlingcorps:SFINAE可以帮助您,这样您就可以完整地保留两个函数签名(我认为这很重要)。例如,您可以在函数模板中使用
std::enable_if
,仅启用所需的情况。是的,如果您碰巧有一个常量值,例如from
const X f();
X x3(f())
。然后,在这种情况下,初始值设定项是一个派生类,它也将使用模板构造函数。下面是与此问题相关的最后一个类:
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
}
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; }