使用模板移动运算符

2023-12-07

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

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 有效。

h 无法编译。 z 是 Foo(float) 类型,我猜这距离 Foo(U)&& 太远了,无法确定如果选择 U=float 则可以调用移动构造函数...

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

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

谢谢


复制或移动构造函数不得是模板。从 12.8(2, 3) 开始:

A 非模板类的构造函数X如果它的第一个参数是类型,则它是一个复制构造函数X&, const X&, volatile X& or const volatile X&,并且要么没有其他参数,要么所有其他参数都有默认参数(8.3.6)。 [例子:X::X(const X&) and X::X(X&,int=1)是复制构造函数。]

A 非模板类的构造函数X是一个移动构造函数,如果它的第一个参数是类型X&&, const X&&, volatile X&&, or const volatile X&&,并且要么没有其他参数,要么所有其他参数都有默认参数(8.3.6)。 [例子:Y::Y(Y&&)是一个移动构造函数。]

所以你的例子f and g工作,因为你正在调用一个普通的构造函数(而不是move-构造函数)。

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

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

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

终于我们明白为什么了h不起作用:表达式的值z in the return语句不能绑定到Foo<float>&&,因为它没有显式转换(通过std::move),也没有考虑到第 12.8(32) 条的特殊豁免,因为它的类型与函数的返回类型不同。 (它只能绑定到Foo<float>&(这几乎肯定是错误的)或Foo<float> const &.)


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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用模板移动运算符 的相关文章

随机推荐