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

Can I reuse an rvalue reference parameter to return an rvalue reference?

本文关键字:引用 返回 形参 我可以      更新时间:2023-10-16

考虑以下代码:

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;

现在,我明白了为什么会这样,以及如何修复它(返回一个值而不是一个右值引用),但我认为这是一个使用错误,如果有人写上面的代码看起来像它在自找麻烦。是否有任何"规范"的现实世界的例子,上面的操作符返回右值引用是一个问题?为什么我应该总是从操作符返回一个右值,一个令人信服的理由是什么?

您正在寻找的示例是基于范围的for语句:

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

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

标准在

中定义了基于范围的for循环
6.5.4基于范围的for语句[stmt.range]

1对于形式为

的基于范围的for语句

for ( for-range-declaration : expression ) statement

range-init等价于用圆括号括起来的表达式

( expression )

和形式为

的基于范围的for语句

for ( for-range-declaration : braced-init-list ) statement

range-init等价于带括号的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返回的临时对象的生命周期,但是它不会延长range-init 内嵌套的临时对象的生命周期。

与其自找麻烦,不如相信字符串自己的move构造函数:

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

现在MyString x = a + b;MyString y = MyString("a") + MyString("b");都有效地工作了