右值引用和左值引用的工作方式是否相同?

3
我想知道我的假设是否正确 -> Rvalue引用的工作方式与lvalue引用类似,只是它们只能绑定到rvalue,因此它们可以用于检测rvalue。
因此,当用作返回类型时,它的工作方式就像lvalue引用一样,就好像它们返回它们绑定到的实际对象一样。
当用作参数类型时,它们传递值而不进行任何复制。
我看到很多人问通过rvalue引用和通过值(以及通过lvalue引用)返回之间的区别是什么,但是难道不能通过说rvalue引用的工作方式类似于lvalue引用来回答所有这些问题吗?因此,如果您知道使用lvalue引用时会发生什么,那么您可能知道使用rvalue引用时会发生什么,即您将知道通过rvalue引用返回实际对象,而通过值返回副本,就像通过lvalue引用和值返回之间的区别一样(大致上)?
还是我错了?

1
我认为那是一个不错的摘要。 - Quentin
我不确定你是否意识到,返回对于局部变量(栈变量)的引用(左值或右值)将会返回一个悬空引用,导致未定义行为。 - Richard Critten
返回一个 r-value 引用会阻止 RVO,因此是一种悲观的优化方式(同时也可能很危险)。 - Richard Hodges
@RichardCritten,我知道 :) 如果本地对象具有自动存储并且将在返回语句处被销毁,那么这也是很有意义的。 - user8221510
@RichardHodges 我认为你假设该函数返回一个局部变量。如果该函数返回一个输入或成员变量的引用(如果它是对象方法),那么右值引用可能是最快的,但仍然有潜在的危险。 - Nir Friedman
@NirFriedman 对的。你可以从特别标记的 r-value 版本的方法中执行此操作,并通过类似 std::move(x).take_thing() 的方式访问它,其中 take_thing 定义为 auto take_thing() && -> X&& {...} - Richard Hodges
2个回答

1
当写入任何命名对象时,它被视为其真实类型。
读取任何对象时,它被视为左值引用。
从函数返回的对象没有名称,因此它被视为其真实类型。
其他所有内容都遵循这个原则。

“real type”是什么意思? - user8221510
@FacPam 我的意思是声明中提到的类型。例如 X、X& 或 X&&。当读取任何这些类型中的任何名称时,它都是一个 X&。 - Richard Hodges

-1

就使用rvalue引用作为返回类型而言,它通常与lvalue引用类似,即通常不应该这样做,因为你不能返回对局部变量的引用。然而,将rvalue引用与输入和输出相结合可以实现一些有趣的事情:

Matrix operator+(const Matrix& lhs, const Matrix& rhs) {
    auto r = lhs;
    r += rhs;
    return r;
}

一些矩阵加法函数的典型签名。但是利用右值引用,我们可以开始添加如下重载:

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

第二个版本很整洁,因为没有副本,甚至没有移动!这在 lvalue 引用中实际上是不可行的,因为您无法确定是否可以直接使用/滥用输入的存储。因此,rvalue 引用确实打开了一些新的通过引用返回的机会,但这仍然是例外而不是规范。

如果我们没有复制省略,那就太棒了,但事实上我们有。因此完全没有必要这样做。在这种情况下,我们应该传递和返回副本。 - Richard Hodges
我不知道17的情况,但在14及其之前返回一个副本并不能总是产生相同的效果。例如,当您链接多个函数调用时,可以看到这一点,复制省略通常只能工作在一层深度。 - Nir Friedman
@RichardHodges 这个程序使用1z编译:http://coliru.stacked-crooked.com/a/87b64891a794dc0f。如果您认为这个程序没有清楚地展示通过rvalue引用传递/返回可以节省移动操作,那么请解释一下。 - Nir Friedman
是的,我明白了,可以通过嵌套操作(例如 w = x + y + z)来优化,但会增加维护开销。 - Richard Hodges

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接