返回c++03与11中的值

Return values in c++03 vs 11

本文关键字:c++03 返回      更新时间:2023-10-16

我花了几个小时讨论右值和左值。以下是我对的理解

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())。

是这样吗?如果没有,请详细说明。

它们不一样。Bar2()在两个标准中都是UB。不能返回通过引用在堆栈上创建的对象。

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

从旧的C++到C++11,右值和左值的概念没有改变。你所描述的"C++03"就是应该发生的事情。在某些情况下,一些编译器优化可以减少不必要的副本数量(包括不必要的复制构造函数调用!),但在其他方面也是如此。

改变的是C++11引入了右值引用(T&&)的概念。

有几篇文章你可以在谷歌上搜索,例如在这里:

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

Bar2()不会在C++2003或C++2011中创建任何副本。对于Bar1(),在C++2003和C++2011中都创建了foo的副本。只有当你确实有一个右值,或者你有一个即将消失的左值,并且它正在被返回时,右值引用的使用才适用。

当然,这个例子恰好是未定义的行为,因为返回的foo就是初始化的foo。也就是说,当返回foo时,您的示例似乎因为没有说明它的含义而变得一团糟。假设每个函数都有一个局部变量foo,根据这两个标准,Bar2()是未定义的行为,Bar1()有些不同:

  • 如果Foo有移动构造函数,C++2011可以使用移动构造函数,而C++2003可以使用复制构造函数
  • 是否使用move构造函数或copy构造函数取决于函数的其余部分和编译器:如果Bar1()中的所有return语句都返回foo,则大多数编译器将忽略额外对象的构造

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

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

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

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

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

这不会执行复制构造函数,而是执行移动构造函数——这应该不会占用太多资源。

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

在C++03中,Bar1()可以复制对象(但这是通过所谓的复制省略进行优化的——请参阅链接)。

在C++11中,基本上没有任何变化。编译器可以调用Foo的复制构造函数或Foo的移动构造函数,也可以执行复制省略。但是,由于即使在C++03中,副本也会被消除,Bar1()在C++11和C++03中也会这样做。

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