【问题标题】:Are Rvalue members indeed Rvalues too?右值成员也确实是右值吗?
【发布时间】:2014-08-06 06:15:37
【问题描述】:

They say Rvalues 的成员也是 Rvalues - 这很有意义。所以这要么是 VC++ 特有的错误,要么是我对 Rvalues 的理解中的错误。

拿这个玩具代码:

#include <vector>
#include <iostream>
using namespace std;

struct MyTypeInner
{
    MyTypeInner()   {};
    ~MyTypeInner()                     { cout << "mt2 dtor" << endl; }
    MyTypeInner(const MyTypeInner& other)  { cout << "mt2 copy ctor" << endl; }
    MyTypeInner(MyTypeInner&& other)       { cout << "mt2 move ctor" << endl; }
    const MyTypeInner& operator = (const MyTypeInner& other)
    {
        cout << "mt2 copy =" << endl;       return *this;
    }
    const MyTypeInner& operator = (MyTypeInner&& other)
    {
        cout << "mt2 move =" << endl;       return *this;
    }
};

struct MyTypeOuter
{
    MyTypeInner mt2;

    MyTypeOuter()   {};
    ~MyTypeOuter()                     { cout << "mt1 dtor" << endl; }
    MyTypeOuter(const MyTypeOuter& other)  { cout << "mt1 copy ctor" << endl;  mt2 = other.mt2; }
    MyTypeOuter(MyTypeOuter&& other)       { cout << "mt1 move ctor" << endl;  mt2 = other.mt2; }
    const MyTypeOuter& operator = (const MyTypeOuter& other)    
    {
        cout << "mt1 copy =" << endl;       mt2 = other.mt2;   return *this;
    }
    const MyTypeOuter& operator = (MyTypeOuter&& other)
    {
        cout << "mt1 move =" << endl;   mt2 = other.mt2;    return *this;
    }
};

MyTypeOuter func()   {  MyTypeOuter mt; return mt; }

int _tmain()
{
    MyTypeOuter mt = func();
    return 0;
}

此代码输出:

mt1 移动 ctor

mt2 复制 =

mt1 dtor

mt2 dtor

即MyTypeOuter的move ctor调用MyTypeInner的copy,而不是move。如果我将代码修改为:

MyTypeOuter(MyTypeOuter&& other)       
{ cout << "mt1 move ctor" << endl;  mt2 = std::move(other.mt2); }

输出如预期:

mt1 移动 ctor

mt2 移动 =

mt1 dtor

mt2 dtor

似乎 VC++(2010 和 2013)不尊重这部分标准。还是我错过了什么?

【问题讨论】:

  • 您是否在编译时进行了全面优化?
  • 没有。随着优化 RVO 的启动,整个移动语义逻辑就像碎肉一样。
  • 您仍然必须在构造函数中使用std::move(other),如果您要执行MyTypeInner mt = func().mt2; 之类的操作,编译器只会将成员视为右值而不进行移动。
  • 那么 rvalue-members 以什么方式本身是 rvalue 呢?我希望这个定义在这里准确地体现出来。在语句 mt2 = other.mt2 中,如果 rhs 确实是右值,则应调用 move 赋值。
  • @OfekShilon other 在该表达式中是左值,除非您移动它,请参阅this 问题。

标签: c++ visual-c++ c++11


【解决方案1】:

rvalue 的成员是否是rvalue 不是这里的问题,因为您在赋值运算符中处理左值。

在这个移动赋值运算符中,

const MyTypeOuter& operator = (MyTypeOuter&& other)
{
    cout << "mt1 move =" << endl;
    mt2 = other.mt2;
    return *this;
}

other 是一个lvalue(因为它有一个名字),通过扩展,other.mt2 也是。当您说mt2 = other.mt2 时,您只能调用标准赋值运算符。

为了调用移动构造函数,你需要让other.mt2看起来像一个右值,这就是std::move实现的:

const MyTypeOuter& operator = (MyTypeOuter&& other)
{
    cout << "mt1 move =" << endl;   
    mt2 = std::move(other.mt2);
    return *this;
}

this related question

【讨论】:

  • 我认为 OP 试图获得的是 std::move(other).mt2 而不是 std::move(other.mt2);,两者都会导致调用移动构造函数,但前者显示了右值的成员本身是右值的(顺便说一下显示了左值的流行定义并不总是成立,因为mt2 有一个名称,但在第一种情况下它不是左值)。
  • 我现在明白了,非常感谢。这似乎仍然是一个奇怪的语言设计选择——它现在迫使我为我的代码中的每种类型添加专门的移动 ctors/赋值,而不是免费享受具有移动语义的库类型的好处。但没关系(我敢肯定去那里会让我们在 1000 个我无法想象的地方字节跳动)。
  • @user657267 我无法理解这个问题。特别是,我没有看到提到std::move(other).mt2
  • @OfekShilon 大多数时候,编译器生成的特殊函数会做正确的事情,所以你没有那么多工作要做。不过,您需要了解有关何时获得免费移动复制 ctor 和移动赋值运算符的规则(或者,更实际地,知道在哪里查找它们。)
猜你喜欢
  • 2022-12-06
  • 2019-04-09
  • 2011-06-14
  • 1970-01-01
  • 1970-01-01
  • 2018-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多