考虑以下:
class Foo { ... };
Foo rbv();
void caller()
{
Foo x = rbv(); ← the return-value of rbv() goes into x
...
}
Foo rbv()
{
...
return Foo(42, 73); ← suppose Foo has a ctor Foo::Foo(int a, int b)
}
将构造多少个 Foo 类型的对象?
考虑以下:
class Foo { ... };
Foo rbv();
void caller()
{
Foo x = rbv(); ← the return-value of rbv() goes into x
...
}
Foo rbv()
{
...
return Foo(42, 73); ← suppose Foo has a ctor Foo::Foo(int a, int b)
}
将构造多少个 Foo 类型的对象?
也许。
在 C++11 中,如果值具有移动构造函数,则通过移动而不是复制返回值。这可能比复制更有效。
在某些情况下 - 例如返回局部变量或临时变量(如您在此处所做的那样) - 可以省略移动或复制。该值直接创建到调用者的堆栈帧中,因此不需要在返回时移动或复制它。喜欢首字母缩略词的人有时称其为 (N)RVO - (Named) Return Value Optimisation。
x
同样,也可以省略从临时返回值到的复制或移动。
任何体面的编译器都会实现这种优化,所以你的代码应该只创建一个Foo
. 您可以通过使析构函数打印一条消息来验证这一点,并观察它只执行一次:http: //ideone.com/xydJqY。如果禁用优化,则最多可能有三个对象。
最多可以有两个副本:一个从临时Foo(42, 73)
到函数的返回值,另一个从函数的返回值到x
. 但是,这两个副本都有资格进行复制省略,这允许不进行复制,而是直接在目标中构造对象,相当于Foo x(42, 73);
.
您可以控制编译器利用复制省略的程度;例如,在 GCC 中,您可以使用-fno-elide-constructors
来获取所有两个副本。