让我们假设我使用Visual Studio或带有-O2的现代GCC。编译器是否会在S
中创建func()
,然后将其复制到my_result
,或者使用构造函数my_result
创建(5, 6, 5 + 6)
而不创建临时S
?
注意: 功能func()
定义及其用法位于单独的.obj文件中!
struct S
{
S(int _x, int _y, int _z) : x(_x), y(_y), z(_z) { }
int x, y, z;
};
S func(int a, int b)
{
return S(a, b, a + b);
}
/// USAGE ///
S my_result = func( 5, 6 );
答案 0 :(得分:7)
现代编译器通常会优化这种操作。见return value optimization
答案 1 :(得分:2)
这是一个优化,根据定义,它几乎意味着它对编译器是可选的,并且由每个特定的编译器决定做什么。你怎么能确定?检查生成代码的反汇编!
那说大多数编译器应该进行这种优化(返回值优化[RVO]),因为在这种情况下相对容易做到(没有多次返回,它是一个未命名的临时值,所以你没有别名等)。
答案 2 :(得分:1)
在我看来,提供的测试用例非常简单,可以RVO申请。
答案 3 :(得分:0)
我怀疑临时是否被优化了。您可以通过在构造函数和复制构造函数中放置print语句来测试它,并查看在不同编译器设置下打印的内容。
答案 4 :(得分:0)
您可以自己测试一下,因为所讨论的优化具有可观察到的差异!
C ++中的大多数形式的优化遵循as-if
规则,这使得它们难以检测。但是,在少数情况下,允许删除(跳过)复制和移动构造函数,即使差异导致可观察到的行为更改。
在这种情况下,请将以下内容添加到S:
struct S {
// ...
S( S const& o ):x(o.x), y(o.y), z(o.z) {
std::cout << "copy ctor!\n";
}
S& operator=( S const& o ) {
x=o.x;
y=o.y;
z=o.z;
std::cout << "copy assign!\n";
return *this;
}
S( S && o ):x(std::move(o.x)), y(std::move(o.y)), z(std::move(o.z)) {
std::cout << "move ctor!\n";
}
S& operator=( S const& o ) {
std::tie( x,y,z ) = std::tie( std::move(o.x),std::move(o.y),std::move(o.z) );
std::cout << "move assign!\n";
return *this;
}
}
并运行您的代码。通过零优化,您将获得副本和/或移动。
通过任何非平凡的优化级别,打印将消失,因为RVO(以及相关的情况下,NRVO)将运行,从而消除副本。 (如果你的编译器不是C ++ 11,删除上面的移动构造函数 - 仍然允许在C ++ 03中进行优化)
在C ++ 11中,您可以通过return {stuff}
语法显式构造返回值,而不是依赖于NRVO / RVO。
请注意,RVO(返回值优化)和NRVO(命名返回值优化)相对脆弱,如果您依赖它们,您必须了解它们的工作原理,使它们断裂的原因以及您的特定编译器的任何怪癖在其实施中(如果有的话)。