我有一个模板化的课程,我想避免复制(因为这样做的潜在成本)。我可以实现一个移动构造函数,但我还想允许移动“accross template parameter”。这是我正在尝试编译的内容:
template <class T>
class Foo
{
public:
Foo() {}
template <class U> Foo(Foo<U>&&) {}
private:
Foo(const Foo&);
};
Foo<int> f() { Foo<float> y; return move(y); }
Foo<int> g() { Foo<int> x; return x; }
Foo<int> h() { Foo<float> z; return z; }
我理解为什么技术上f编译:移动类型(y)是Foo(浮动)&amp;&amp;并且碰巧有一个方便的构造函数接受Foo(U)&amp;&amp;,因此编译器设法找到U = float工作。
h无法编译。 z的类型为Foo(浮点数),我猜这离Foo(U)&amp;&amp;要弄清楚如果选择U = float就可以调用移动构造函数......
我不确定为什么g编译,但确实如此。 x的类型是Foo(int)。编译器如何设法使用move运算符(它不能只是从Foo(int)隐式转换为Foo(int)&amp;&amp;,可以吗?)
所以我的问题是:规则是什么?为什么h编译但g不?有什么我可以在Foo中改变以使其编译吗?
谢谢
答案 0 :(得分:6)
复制或移动构造函数不能是模板。从12.8(2,3):
如果第一个参数类型为
X
,X&
,const X&
,则类volatile X&
的非模板构造函数是一个复制构造函数或const volatile X&
,或者没有其他参数,或者所有其他参数都有默认参数(8.3.6)。 [示例:X::X(const X&)
和X::X(X&,int=1)
是复制构造函数。]类
X
的非模板构造函数是移动构造函数,如果其第一个参数类型为X&&
,const X&&
,volatile X&&
,或const volatile X&&
,并且没有其他参数,或者所有其他参数都有默认参数(8.3.6)。 [示例:Y::Y(Y&&)
是移动构造函数。]
因此,您的示例f
和g
有效,因为您正在调用普通构造函数(而不是 move --constructor)。
f
有明显原因,因为move(y)
的结果可以绑定到Foo<float>&&
。 g
的作用原因不同:由于x
的类型与函数的返回类型相同,因此返回语句中表达式x
的值与{{1}匹配}。这是因为12.8(31,32):
在具有类返回类型的函数中的
Foo<int>&&
语句中,当表达式是非易失性自动对象的名称(除了函数或catch子句参数)具有相同的cv-unqualified type作为函数返回类型,[...]当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策以选择构造函数首先执行复制,就像对象是由右值指定一样。
最后,我们了解为什么return
不起作用:h
语句中的z
表达式的值无法绑定到return
,因为它不是Foo<float>&&
显式转换(通过std::move
),也没有给出第12.8(32)条的特殊规定,因为它的类型与函数的返回类型不同。 (它只能绑定到Foo<float>&
(这几乎肯定是错误的)或Foo<float> const &
。)
顺便说一句,没有必要移动不管理外部资源(例如图元)的对象。无论如何都必须复制实际的对象数据。