【问题标题】:Can I reuse an rvalue reference parameter to return an rvalue reference?我可以重复使用右值引用参数来返回右值引用吗?
【发布时间】:2013-04-20 00:29:33
【问题描述】:

考虑以下代码:

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 而不是一个右值引用),但是如果有人编写上面的代码,我认为这是一个使用错误,因为代码看起来像是在自找麻烦。是否有任何“规范”真实世界的示例,其中上述运算符返回右值引用是一个问题?为什么我应该始终从运算符返回右值,有什么令人信服的理由?

【问题讨论】:

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


【解决方案1】:

您要查找的示例是基于范围的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.ranged]

1 对于基于范围的for 形式的语句

for (for-range-declaration:表达式)声明

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

( expression )

对于基于范围的for 形式的声明

for (for-range-declaration:braced-init-list)声明

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 返回的临时对象的生命周期,但不会延长 inside 的嵌套临时对象的生命周期范围初始化

【讨论】:

    【解决方案2】:

    你应该相信字符串自己的移动构造函数,而不是自找麻烦:

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

    现在MyString x = a + b;MyString y = MyString("a") + MyString("b"); 都可以高效工作。

    【讨论】:

    • 对于x = a + b,创建了一个不需要的b 的额外副本。但正如我所说,我知道如何解决它,问题是为什么返回右值引用是一个问题。
    • @DanielFrey:是的,我想,两者中的一个可能是引用...返回引用只有在你能保证引用引用存在的东西时才可以,这很难而且对用户来说不明显。
    • @DanielFrey:由于在前面附加可能不值得优化,我可能只将第二个参数更改为const MyString &,就像您在原始代码中所做的那样。
    • @DanielFrey 通过值传递而不是通过引用在 Going Native 2012 - STL11 Magic && Secrets 处呈现,从 35:10 标记开始。没看过的也值得一看。
    猜你喜欢
    • 1970-01-01
    • 2015-07-17
    • 1970-01-01
    • 1970-01-01
    • 2014-09-09
    • 1970-01-01
    • 2018-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多