创建一个对象,局部变量与右值引用

Creating an object, local variable vs rvalue reference

创建对象时使用 r 值引用是否有任何优势,否则该对象将位于普通局部变量中?

Foo&& cn = Foo();
cn.m_flag = 1;
bar.m_foo = std::move(cn);
//cn is not used again

Foo cn;
cn.m_flag = 1;
bar.m_foo = std::move(cn); //Is it ok to move a non rvalue reference?
//cn is not used again

在第一个代码片段中,显然不会有任何副本,但我猜想在第二个代码片段中编译会优化副本吗?

同样在第一个片段中,对象实际存储在内存中的什么位置(在第二个片段中,它存储在封闭函数的堆栈帧中)?

这些代码片段大部分是等价的。这个:

Foo&& rf = Foo();

正在将临时对象绑定到引用,从而将临时对象的生命周期延长到引用的生命周期。 Foo 只会在 rf 超出范围时被销毁。这与您得到的行为相同:

Foo f;

除了在后一个示例中 f 是默认初始化的,而在前一个示例中 rf 是值初始化的。对于某些类型,两者是等效的。对于其他人,他们不是。如果您改为写 Foo f{},那么这种差异就会消失。

剩下的一个区别与复制省略有关:

Foo give_a_foo_rv() {
    Foo&& rf = Foo();
    return rf;
}

Foo give_a_foo() {
    Foo f{};
    return f;
}

第一个例子不允许执行RVO,因为rfgive_a_foo_rv()的return类型不相同。此外,rf 甚至不会自动移动到 return 类型,因为它不是一个对象,所以它没有自动存储持续时间,所以这是一个额外的副本(直到 C++20,在这是一个额外的举动):

Foo f = give_a_foo_rv(); // a copy happens here!
Foo g = give_a_foo();    // no move or copy

it seems clear that there will not be any copies

这完全取决于移动 Foo 的实际作用。如果 Foo 看起来像:

struct Foo {
    Foo() = default;
    Foo(Foo const& ) = default;
    Foo& operator=(Foo const& ) = default;

    // some members
};

然后移动 Foo 仍然进行复制。


是的,在第二个例子中 std::move(f) 完全没问题。您不需要从 Tmove 的类型右值引用的对象。这将严重限制移动的实用性。