按值与右值引用返回

Returning by value vs rvalue reference

本文关键字:返回 引用      更新时间:2023-10-16

在回答有关返回临时工的问题后,我注意到有第二个回复略有不同。

它不是按值返回,而是按右值引用返回。 您能解释一下这些方法之间的区别以及它们的风险在哪里吗?

struct Bar
{
Bar& doThings() & {return *this;}
// Returning rvalue reference
Bar&& doThings() &&    {return std::move(*this);}
// Alternative: Returning by value
//Bar doThings() && {return std::move(*this);}
std::unique_ptr<int> m_content; // A non-copyable type
};

一个主要区别是,如果返回右值引用,则当返回值绑定到引用时,临时的生存期将不会延长。

例如

Bar&& rb = Bar{}.doThings();
use(rb); // not safe if returning rvalue reference, while safe if returning by value

我对此很感兴趣,所以我敲了一个小测试程序:

#include <memory>
#include <iostream>
struct Bar
{
// Return by reference
Bar& doThings() & { return *this; }
// Return by rvalue reference
Bar&& doThings() && { std::cout << "in Bar&& doThings, this=" << (void *) this << "n"; return std::move (*this); }
~Bar () { std::cout << "Bar destroyed at " << (void *) this << "n"; }
int first;
std::unique_ptr<int> m_content; // Make Bar a non-copyable type
};
int main ()
{
std::cout << std::hex;
Bar bar;
std::cout << "bar is at " << &bar << "n";
Bar& bar_ref = bar.doThings ();
std::cout << "bar_ref refers to " << &bar_ref.first << "nn";
Bar&& bar_rvalue_ref = Bar ().doThings ();
std::cout << "bar_rvalue_ref refers to " << &bar_rvalue_ref.first << "nn";
}

输出:

bar is at 0x7ffdc10846f0
bar_ref refers to 0x7ffdc10846f0
in Bar&& doThings, this=0x7ffdc1084700
Bar destroyed at 0x7ffdc1084700
bar_rvalue_ref refers to 0x7ffdc1084700
Bar destroyed at 0x7ffdc10846f0

因此,这告诉我们,这两种形式的doThings()都不会产生暂时性。 您也可以在Godbolt上验证这一点。

那么,看看所写的Bar的实施,这给我们留下了什么? 好吧,我个人根本看不出Bar&& doThings()有任何意义,因为:

  1. AFAICT,您只能在新构建的对象上调用它,因此无论如何它似乎没有什么意义。
  2. 因为它从*this移动,所以原则上它吃掉了新构造的对象,所以返回的引用(仍然引用它(是不依赖的。
  3. 正如@xskxzr已经指出的那样,在步骤 1 中创建的临时具有无限小的生存期。

然而,Bar& doThings()工作得很好(并且实际上返回指向对象本身的指针,因此不会涉及复制或临时(。

我确定我错过了一些重要的东西,所以我很想听听人们对此的反应。 当然,通过右值引用返回是有一定目的的,也许在更复杂的情况下。 谢谢。

现场演示