模板构造器优先于正常复制和移动构造函数?

时间:2012-12-31 10:03:55

标签: 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&&) { cout << "tmpl" << endl; }
};

int main()
{
    X x1 = 42;
    X x2(x1);
}

tmpl
tmpl

所需的输出是:

tmpl
copy

为什么具体的复制构造函数不优先于模板构造函数?

无论如何都要修复它,以便复制和移动构造函数重载优先于模板构造函数?

3 个答案:

答案 0 :(得分:5)

嗯,这是因为reference-collapsing

在重载解析阶段,当实例化函数模板时,T被推导为X&,因此T&&X& &&)变为{{1}由于引用 - 折叠,函数模板中的实例化函数变为完全匹配,复制构造函数需要X&到{{}的转换1}}(这就是选择劣等匹配的原因。)

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

const X&

Output正如所料。

或者,如果你将函数模板中的参数设为const,那么将调用copy-constructor(即使它保持不变!),因为引用折叠现在不会出现:

X(/*const*/ X&) { cout << "copy" << endl; }
预计会再次

Output

答案 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
}