我花了几个小时关于 rvalue 和左值。这是我理解的
int main()
{
//.....
Foo foo = Bar1();
foo = Bar2();
//......
}
Foo Bar1()
{
//Do something including create foo
return foo;
}
Foo& Bar2()
{
//Do something including create foo
return foo;
}
在c ++ 03下,Bar1()
将复制返回对象(就在返回之前),然后返回复制对象的地址;执行一个即将被销毁的物品的浪费副本。 Bar2()
将返回在函数中创建的对象。
在c ++ 11下,Bar1()
和Bar2()
基本上是等价的(也等同于c ++ 03的Bar2()
)。
是吗?如果没有,请详细说明。
答案 0 :(得分:6)
他们不一样。 Bar2()
是两个标准的UB。您不能通过引用返回在堆栈上创建的对象。
在C ++ 03 Bar1()
中可能会利用RVO并且不会复制任何内容。在C ++ 11中,Bar1()
甚至会使用RVO,或者如果无法使用RVO,则会使用移动构造函数。
答案 1 :(得分:1)
rvalues和左值的概念并没有从旧的C ++变为C ++ 11。您所描述的“C ++ 03”应该是什么。在某些情况下,某些编译器优化可以减少不必要的副本数量(包括不必要的复制构造函数调用!),但除此之外它们是相同的。
更改的是C ++ 11引入了rvalue-reference(T&&
)的概念。
有几篇文章你可以谷歌,例如在这里:
http://thbecker.net/articles/rvalue_references/section_01.html
答案 2 :(得分:1)
Bar2()
不会在C ++ 2003或C ++ 2011中创建任何副本。对于Bar1()
,在{+ C} 2003和C ++ 2011中都会创建foo
的副本。使用rvalue引用仅适用于您实际拥有右值的情况,或者如果您的左值很快就会消失并且正在返回。
当然,该示例恰好是未定义的行为,因为返回的foo
是正在初始化的foo
。也就是说,似乎你的例子是因为没有说明foo
返回时的意图而搞砸了。假设每个函数都有一个局部变量foo
,Bar2()
根据两个标准都是未定义的行为,Bar1()
有些不同:
Foo
的移动构造函数,C ++ 2011可能会使用移动构造函数,而C ++ 2003可能会使用复制构造函数。return
中的所有Bar1()
语句都返回foo
,则构造一个额外的大多数编译器都会省略对象。答案 3 :(得分:0)
Bar2()将返回在函数中创建的对象。
这显然是错的。 Bar2()
会将引用返回给某个对象。 (注意:如果在Bar2()
)内的堆栈上创建了对象,那么它将是UB。
在c ++ 11下,Bar1()和Bar2()基本上是等价的(也等同于c ++ 03的Bar2())。
在C ++ 11下,意思是一样的。你真正感兴趣的是移动语义:
Foo Bar3()
{
//Do something
return std::move(foo);
}
这不会执行复制构造函数,而是执行一个移动构造函数 - 这应该会减少资源消耗。
答案 4 :(得分:0)
这篇文章对您有用:Want Speed? Pass by Value.
在C ++ 03中,Bar1()
可能会复制对象(但这会被所谓的复制省略优化 - 请参阅链接)。
在C ++ 11中,基本上没有任何改变。允许编译器调用Foo
的复制构造函数或Foo
的移动构造函数或复制省略。但是,即使在C ++ 03中,副本也会被省略,Bar1()
在C ++ 11和C ++ 03中也是如此。
Bar2()
只返回一个引用,C ++ 11中没有什么不同。返回引用可能很关键。如果foo
是本地值,则返回对已经销毁的变量的引用,永远不会返回。