【问题标题】:Move operator with templates使用模板移动运算符
【发布时间】:2012-06-17 05:45:46
【问题描述】:

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

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 编译的吗?

谢谢

【问题讨论】:

  • 我只是尝试用 MSVC、Clang 和 GCC 编译它 - 全部编译了这 3 个函数。

标签: c++ templates c++11 move-semantics


【解决方案1】:

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

X非模板构造函数是一个复制构造函数,如果它的第一个参数是X&amp;const X&amp;volatile X&amp;const volatile X&amp;类型,或者有没有其他参数,否则所有其他参数都有默认参数(8.3.6)。 [例如:X::X(const X&amp;)X::X(X&amp;,int=1) 是复制构造函数。]

X非模板构造函数是移动构造函数,如果它的第一个参数是X&amp;&amp;const X&amp;&amp;volatile X&amp;&amp;const volatile X&amp;&amp;类型,或者没有其他参数,否则所有其他参数都有默认参数(8.3.6)。 [示例:Y::Y(Y&amp;&amp;) 是一个移动构造函数。]

所以您的示例 fg 有效,因为您正在调用一个普通的构造函数(而不是 move-constructor)。

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

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

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

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


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

【讨论】:

  • @anonymous:嗯,你是对的,你确实有一个用户定义的构造函数。
  • @anonymous:您的g 实际上正在使用模板。我现在找到了真正的原因;编辑。
  • 谢谢!这就说得通了。我可以做些什么来使 h 编译和使用移动语义?
  • @anonymous:像f 那样说std::move 有什么问题?这看起来是最直接的解决方案。
  • 我确实想提供一个从 Foo(U) 到 Foo(T) 的复制构造函数,这将使 h 编译但不使用 move 运算符。我非常确信我班的任何用户都不会检查他们正在使用的运算符并添加移动,如果他们编写看起来像 h 的代码,这意味着他们最终会调用更昂贵的复制构造函数......
猜你喜欢
  • 1970-01-01
  • 2018-08-03
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2020-10-17
  • 2023-03-03
相关资源
最近更新 更多