创建一个对象,局部变量vs右值参考

时间:2016-09-07 12:38:09

标签: c++ 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

在第一个代码片段中,似乎很明显没有任何副本,但我猜在第二个编译会优化副本吗?

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

1 个答案:

答案 0 :(得分:10)

这些代码片段大多是等效的。这样:

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()的返回类型的类型不同。此外,rf甚至不会自动移入返回类型,因为它不是一个对象,所以它没有自动存储持续时间,因此这是一个额外的副本:

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

似乎很清楚,不会有任何副本

这完全取决于Foo实际行动的内容。如果Foo看起来像:

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

    // some members
};

然后移动Foo仍然会复制。

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