0

我花了几个小时来讨论rvalue和 lvalue。这是我的理解

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()会复制返回对象(就在return之前),然后返回被复制对象的地址;执行一个即将被销毁的对象的浪费副本。Bar2()将返回在函数中创建的对象。

在 c++11 下,Bar1()本质Bar2()上是等价的(也等价于Bar2()c++03)。

是对的吗?如果不是,请详细说明。

4

5 回答 5

6

它们是不相同的。Bar2()两种标准都是UB。您不能通过引用返回在堆栈上创建的对象。

在 C++03Bar1()中可以利用 RVO 并且不会复制任何内容。在 C++11Bar1()中甚至会使用 RVO,或者如果 RVO 不可用,将使用移动构造函数。

于 2012-12-06T22:40:33.600 回答
1

右值和左值的概念从旧的 C++ 到 C++11 并没有改变。你所描述的“C++03”是应该发生的。在某些情况下,一些编译器优化可以减少不必要的副本数量(包括不必要的复制构造函数调用!),但除此之外它是相同的。

改变的是 C++11 引入了 rvalue-reference ( T&&) 的概念。

有几篇关于它的文章,你可以用谷歌搜索,例如在这里:

http://thbecker.net/articles/rvalue_references/section_01.html

于 2012-12-06T22:40:53.333 回答
1

Bar2()不会在 C++ 2003 或 C++ 2011 中创建任何副本。对于在 C++ 2003 和 C++ 2011 中创建Bar1()的副本。foo仅当您确实有一个右值或者如果您有一个左值时,使用右值引用才适用即将消失,它正在被归还。

当然,该示例恰好是未定义的行为,因为foo正在返回的是foo正在初始化。foo也就是说,您的示例似乎因为没有说明返回时的含义而搞砸了。假设每个函数都有一个局部变量fooBar2()根据两种标准都是未定义的行为,并且Bar1()有些不同:

  • 如果有 的移动构造函数Foo,C++ 2011 可能使用移动构造函数,而 C++ 2003 可能使用复制构造函数。
  • 是否使用移动构造函数或复制构造函数取决于函数的其余部分和编译器:如果return中的所有return语句,则大多数编译器将省略额外对象的构造。Bar1()foo
于 2012-12-06T22:50:26.127 回答
0

Bar2() 将返回在函数中创建的对象。

这显然是错误的。Bar2()将返回对某个对象的引用。(注意:如果对象是在里面的堆栈上创建的,那将是 UB Bar2())。

在 c++11 下,Bar1() 和 Bar2() 本质上是等价的(也等价于 c++03 的 Bar2())。

在 C++11 下含义相同。您真正感兴趣的是移动语义:

Foo Bar3()
{
  //Do something
  return std::move(foo);
}

这不会执行复制构造函数,而是执行移动构造函数 - 这应该更少资源消耗。

于 2012-12-06T22:43:25.217 回答
0

这篇文章可能对您来说很有趣:想要速度吗?按值传递。

在 C++03 中,Bar1()可以复制对象(但这已被优化为所谓的复制省略——参见链接)。

在 C++11 中,基本上没有任何改变。允许编译器调用Foo的复制构造函数或Foo移动构造函数或进行复制省略。但由于即使在 C++03 中副本也会被忽略,Bar1()所以在 C++11 和 C++03 中也是如此。

Bar2()返回只是一个引用,在 C++11 中没有什么不同。返回引用可能很关键。如果foo将是一个局部值,则返回对已销毁变量的引用,而不是 this。

于 2012-12-06T22:46:36.303 回答