模板构造函数优先于普通复制和移动构造函数
Template Constructor Taking Precedence Over Normal Copy and Move Constructor?
下面程序的输出…
#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
为什么具体复制构造函数没有优先于模板构造函数?
是否有办法修复它,以便复制和移动构造函数重载将优先于模板构造函数?
嗯,这是因为引用崩溃。
在重载解析阶段,当函数模板实例化时,T
被扣除为X&
,因此T&&
(即X& &&
)由于引用坍缩而成为X&
,并且从函数模板实例化的函数变成精确匹配,复制构造函数需要从X&
转换到const X&
(这就是为什么它没有被选择为劣质匹配)。
但是,如果从复制构造函数中删除const
,则首选复制构造函数。试试这个:
X(/*const*/ X&) { cout << "copy" << endl; }
输出符合预期。
或者,如果您将函数模板中的参数设置为const T&
,则将调用复制构造函数(即使它保持不变!),因为现在不会出现引用崩溃:
template<class T> X(const T &) { cout << "tmpl" << 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; }
只需要添加一个dummy
默认模板参数(一种已知的技术:Link)。不需要额外的头文件。
您将得到所需的输出。
我从一个相关的问题中得到了这个答案:链接。所有这些看起来相当不优雅,但似乎是目前唯一的出路。我仍然希望看到一个更优雅的解决方案
正常的重载解析规则在选择构造函数时仍然适用,并且构造函数使用非const左值引用(对于参数推导后的模板构造函数)比使用const左值引用的构造函数更匹配。
当然,您可以添加另一个接受非const左值引用的重载,即
X(X&) { cout << "copy" << endl; }
Update:其他模板构造函数更匹配的情况:
const X f()
{ return X(); }
struct Y : X
{ Y() { } };
int main()
{
X x3(f()); // const-qualified rvalue
Y y;
X x4(y); // derived class
}
相关文章:
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 使用复制构造函数复制双精度数组
- C 无可行的构造函数复制类型的变量
- 没有可行的构造函数复制类型 'MyString' 的数组元素
- 编译时,复制构造函数/复制分配和正常功能调用优化之间是否存在任何区别
- 如何最小化调用列表构造函数(复制构造函数)的次数?
- C 11矢量构造函数复制与范围
- 我定义了一个非复制构造函数;复制构造函数还会被隐式定义吗
- 可以将构造函数复制为转换运算符
- 将基类指针的构造函数复制到子类
- C++树类:构造函数/复制/内存泄漏
- 如何制作这个在模板构造函数复制中使用类型定义的类型的模板
- 将构造函数复制为模板化的成员函数
- 绕过私有复制构造函数/复制赋值C++
- C++通过构造函数复制对象
- 复制构造函数 - 复制C++中的对象
- 将带unique_ptr的类的构造函数复制到作为成员的抽象类