我可以重用rvalue引用参数来返回右值引用吗?

时间:2013-04-25 23:02:15

标签: c++ c++11 operator-overloading rvalue-reference

请考虑以下代码:

struct MyString
{
  // some ctors

  MyString& operator+=( const MyString& other ); // implemented correctly
};

MyString operator+( const MyString& lhs, const MyString& rhs )
{
  MyString nrv( lhs );
  nrv += rhs;
  return nrv;
}

MyString&& operator+( MyString&& lhs, const MyString& rhs )
{
  lhs += rhs;
  return std::move( lhs ); // return the rvalue reference we received as a parameter!
}

这适用于以下用例

MyString a, b, c; // initialized properly
MyString result = a + b + c;

但它为

创造了一个悬垂的参考
const MyString& result = a + b + c;

现在,我理解为什么会这样,以及如何修复它(返回一个ravlue而不是rvalue引用)但我认为如果有人写上面的代码看起来像是在寻找麻烦,这是一个使用错误。是否有任何“规范”的现实示例,其中上述运算符返回右值引用是一个问题?为什么我总是应该从运算符返回一个rvalue有什么令人信服的理由?

2 个答案:

答案 0 :(得分:9)

您要查找的示例是基于范围的for声明

MyString a, b, c;
for( MyCharacter mc : a + b + c ) { ... }

在这种情况下,a + b + c的结果绑定到引用,但嵌套的临时(由a + b生成并作为(a + b) + c的右值引用返回)在范围之前被销毁基于for循环执行。

标准在

中定义了基于范围的for循环
  

6.5.4基于范围的语句[stmt.ranged]

     

1 对于表单

的基于范围的for语句      

for ( 换范围声明 : 表达 ) 语句

     

range-init 等同于括号所包围的表达式

     

( expression )

     

以及表单

的基于范围的for语句      

for ( 换范围声明 : 支撑-INIT列表 ) 语句

     

range-init 等同于 braced-init-list 。在每种情况下,基于范围的for语句等同于

{
   auto && __range = range-init;
   for ( auto __begin = begin-expr,
              __end = end-expr;
         __begin != __end;
         ++__begin ) {
      for-range-declaration = *__begin;
      statement
   }
}

请注意,auto && __range = range-init;会延长从 range-init 返回的临时值的生命周期,但它不会延长< 内嵌套临时值的生命周期EM>范围-INIT

答案 1 :(得分:5)

您应该信任字符串自己的移动构造函数,而不是要求麻烦:

MyString operator+(MyString lhs, MyString rhs)
{
    lhs += std::move(rhs);
    return std::move(lhs);
}

现在,MyString x = a + b;MyString y = MyString("a") + MyString("b");都能有效运作。