C++ 创建对象、局部变量与右值引用

C++ 创建对象、局部变量与右值引用,c++,rvalue-reference,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 在第一个代

在创建对象时使用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();
将临时对象绑定到引用,从而将临时对象的生存期延长到引用的生存期。只有当
rf
超出范围时,
Foo
才会被销毁。这与您的行为相同:

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,因为
rf
的返回类型与
give_a\u foo\u rv()
的返回类型不同。此外,
rf
甚至不会自动移动到返回类型中,因为它不是一个对象,所以它没有自动存储持续时间,所以这是一个额外的副本(直到C++20,在其中它是一个额外的移动):


很明显,不会有任何副本

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

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

    // some members
};
然后移动
Foo
仍会进行复制



是的,在第二个示例中,完全可以
std::move(f)
。您不需要从
t
move
类型为rvalue reference的对象。这将严重限制移动的有效性。

您的第一部分甚至不应该编译。第二部分正是你想要的。std::move使cn变成一个右值,然后将其移动到m_foo(假设move操作符没有被删除等等)。@Hayt,为什么它不应该编译?据我所知,将一个临时变量赋值给一个r值ref,可以延长临时变量的使用寿命(如果它是一个正常的引用,那么是的,它不会编译)啊,好的。只是从来没见过有人用这种方式编码(其实没必要)。但仍然移动主要是为了使非右值对象变成右值对象。
struct Foo {
    Foo() = default;
    Foo(Foo const& ) = default;
    Foo& operator=(Foo const& ) = default;

    // some members
};