使用模板移动运算符

Move operator with templates

本文关键字:移动 运算符      更新时间:2023-10-16

我有一个模板化类,我想避免复制(因为这样做的潜在成本)。我可以实现一个移动构造函数,但我也想允许"跨模板参数"移动。这是我正在尝试编译的内容:

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编译:move(y)的类型是Foo(float)&&&,并且恰好有一个方便的构造函数需要Foo(U)&&,所以编译器设法找到U=float工作。

z 是 Foo(

float) 的类型,我想这与 Foo(U)&&&相去甚远,无法弄清楚如果选择 U=float,则可以调用移动构造函数......

我不确定为什么 g 编译,但它确实如此。x 的类型是 Foo(int)。编译器如何设法使用移动运算符(它不能只是从 Foo(int) 隐式转换为 Foo(int)&&,不是吗?

所以我的问题是:规则是什么? 为什么 h 可以编译,而 g 不会? 我可以在 Foo 中更改一些东西来使 h 编译吗?

谢谢

复制或移动构造函数不能是模板。从 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工作,因为您正在调用普通构造函数(而不是移动构造函数)。

f工作的原因很明显,因为move(y)的结果可以绑定到Foo<float>&&g工作的原因不同:由于x的类型与函数的返回类型相同,因此 return 语句中表达式x的值与Foo<int>&&匹配。这是因为 12.8(31, 32):

在具有类返回类型的函数的 return 语句中,当表达式是与函数返回类型具有相同 CV 非限定类型的非易失性自动对象(函数或 catch 子句参数除外)的名称时,[...]

当满足或将满足复制操作省略的条件(源对象是函数参数,并且要复制的对象由左值指定)时,首先执行重载解析以选择复制的构造函数,就像对象由右值指定一样。

最后,我们明白为什么h不起作用:return语句中表达式z的值不能绑定到Foo<float>&&,因为它没有显式强制转换(通过std::move),也没有给出条款12.8(32)的特殊分配,因为它的类型与函数的返回类型不同。 (它只能绑定到Foo<float>&(这几乎肯定是错误的)或Foo<float> const &


顺便说一下,移动不管理外部资源的对象(例如基元)是没有意义的。无论如何,必须复制实际的对象数据。