6

以下程序的输出...

#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

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

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

4

3 回答 3

5

嗯,这是因为reference-collapsing

在重载解析阶段,当函数模板被实例化时,T被推断为X&,因此T&&(即X& &&)成为X&由于引用折叠,并且从函数模板中实例化的函数变为精确匹配并且复制构造函数需要X&to const X&(这就是为什么不选择劣质匹配的原因)。

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

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

按预期输出。

或者,如果您将函数模板中的参数设置为 asconst T&然后将调用复制构造函数(即使它保持不变!),因为引用折叠现在不会出现:

template<class T> X(const T &) { cout << "tmpl" << endl; }

再次预期输出。

于 2012-12-31T10:13:06.267 回答
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。这一切看起来相当不雅,但似乎是目前唯一的出路。我仍然希望看到一个更优雅的解决方案。

于 2012-12-31T10:50:32.873 回答
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
}
于 2012-12-31T10:14:30.327 回答