[英]How many temporary objects are created when two objects are added together without the return value optimization?
I decided to ask this question after reading items 20 and 22 of the book "More Effective C++" by Scott Meyers. 在阅读Scott Meyers所着的“更有效的C ++”一书的第20和22项后,我决定提出这个问题。
Let's say you wrote a class to represent rational numbers: 假设你写了一个代表有理数的类:
class Rational
{
public:
Rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;
Rational& operator+=(const Rational& rhs); // Does not create any temporary objects
...
};
Now let's say that you decided to implement operator+
using operator+=
: 现在让我们假设您决定使用
operator+=
实现operator+
:
const Rational operator+(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs) += rhs;
}
My question is: if the return value optimization were disabled, how many temporary variables would be created by operator+
? 我的问题是:如果禁用了返回值优化 ,
operator+
将创建多少个临时变量?
Rational result, a, b;
...
result = a + b;
I believe 2 temporaries are created: one when Rational(lhs)
is executed inside the body of operator+
, and another when the value returned by operator+
is created by copying the first temporary. 我相信创建了2个临时值:一个是在
operator+
的主体内执行Rational(lhs)
,另一个是在operator+
返回的值是通过复制第一个临时值来创建的。
My confusion arose when Scott presented this operation: 当Scott提出这个操作时,我的困惑出现了:
Rational result, a, b, c, d;
...
result = a + b + c + d;
And wrote: "Probably uses 3 temporary objects, one for each call to operator+
". 并写道:“可能使用3个临时对象,每个调用一个
operator+
”。 I believe that if the return value optimization were disabled, the operation above would use 6 temporary objects (2 for each call to operator+
), while if it were enabled, the operation above would use no temporaries at all. 我相信如果禁用返回值优化 ,上面的操作将使用6个临时对象(每次调用
operator+
),而如果启用,则上面的操作根本不使用临时值。 How did Scott arrive at his result? 斯科特是如何得出他的结果的? I think the only way to do so would be to partially apply the return value optimization .
我认为这样做的唯一方法是部分应用返回值优化 。
I think you're just considering too much, especially with the details of optimization. 我认为你只是在考虑太多,尤其是优化的细节。
For result = a + b + c + d;
result = a + b + c + d;
, the author just want to state that 3 temporaries will be created, the 1st one is for the result of a + b
, then 2nd one is for the result of temporary#1 + c
, the 3rd one is for temporary#2 + d
and then it's assigned to result
. ,作者只想说明将创建3个临时值,第1个是
a + b
的结果,第2个是temporary#1 + c
的结果,第3个是temporary#2 + d
然后它被分配给result
。 After that, 3 temporaries are destroyed. 在那之后,3个临时工被摧毁。 All the temporaries are only used as the intermediate results.
所有临时工作仅用作中间结果。
On the other hand, some idioms such like expression templates could make it possible to get the final result directly with elimination of temporaries. 另一方面,像表达模板这样的一些习语可以直接通过消除临时性来获得最终结果。
Compiler may detect accumulation and apply optimizations but generally shifting and reducing an expression from left to right is somehow tricky as it may be hit by an expression of style a + b * c * d 编译器可以检测累积并应用优化,但通常从左到右移位和减少表达式在某种程度上是棘手的,因为它可能被样式a + b * c * d的表达式击中
It is more cautious to take approach of form: 采取形式更谨慎:
a + (b + (c + d))
a +(b +(c + d))
which will not consume a variable before it might be required by an operator with higher priority. 在优先级较高的运营商可能需要之前,它不会消耗变量。 But evaluating it requires temporaries.
但是评估它需要时间。
No variables are created by the compiler. 编译器不会创建任何变量。 Because variables are those appearing in the source code, and variables don't exist at execution time , or in the executable (they might become memory locations, or be "ignored").
因为变量是出现在源代码中的变量 ,并且变量在执行时或可执行文件中不存在 (它们可能成为内存位置,或者被“忽略”)。
Read about the as-if rule . 阅读as-if规则 。 Compilers are often optimizing .
编译器经常进行优化 。
See CppCon 2017 Matt Godbolt “What Has My Compiler Done for Me Lately? 请参阅CppCon 2017 Matt Godbolt“我的编译器最近为我做了什么? Unbolting the Compiler's Lid” talk .
解开编译器的盖子“谈话 。
In the expression a+b+c+d
6 temporaries will be created and destroyed, this is mandatory (with and without RVO). 在表达式
a+b+c+d
6中将创建和销毁临时数,这是强制性的(有和没有RVO)。 You can check it here . 你可以在这里查看 。
Inside operator +
definition, in the expression Rational(lhs)+=a
, the prvalue Rational(lhs)
will be bound to the implied object parameter of operator+=
which is authorized according to this very specific rule [over.match.func]/5.1 (refered in [expr.call]/4 ) 在
operator +
definition中,在表达式Rational(lhs)+=a
,prvalue Rational(lhs)
将绑定到operator+=
的隐含对象参数 ,该参数根据这个非常具体的规则[over.match.func] /授权5.1 (参见[expr.call] / 4 )
even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter.
即使隐式对象参数不是const限定的,也可以将rvalue绑定到参数,只要在所有其他方面,参数可以转换为隐式对象参数的类型。
Then to bind a prvalue to a reference, temporary materialization must occurs [class.temporary]/2.1 然后将prvalue绑定到引用,必须进行临时实现[class.temporary] /2.1
Temporary objects are materialized [...]:
临时物品已实现[...]:
- when binding a reference to a prvalue
将引用绑定到prvalue时
So a temporary is created during the excution of each operator +
call. 因此,在每个
operator +
呼叫的执行期间创建临时。
Then the expression Rational(lhs)+=a
which once is returned can conceptualy seen as Rational(Rational(lhs)+=a)
is a prvalue (a prvalue is an expression whose evaluation initializes an object - phi:an object in power) which is then bound to the first argument of the 2 subsequent calls to operator +
. 然后表达式
Rational(lhs)+=a
一次返回可以概念性地看作Rational(Rational(lhs)+=a)
是一个prvalue( prvalue是一个表达式,其评估初始化一个对象 - phi:一个权力对象)然后绑定到后续2次调用operator +
的第一个参数。 The cited rule [class.temporary]/2.1 applies twice again and will create 2 temporaries: 引用的规则[class.temporary] /2.1再次应用两次,将创建2个临时值:
a+b
, a+b
的结果, (a+b)+c
(a+b)+c
So at this point 4 temporaries have been created. 所以在这一点上已经创造了4个临时工。 Then, the third call to
operator+
creates the 5th temporary inside the function body 然后,第三次调用
operator+
在函数体内创建第5个临时值
Finaly the result of the last call to operator +
is a discarded value expression . 最后一次调用
operator +
是丢弃的值表达式 。 This last rule of the standard applies [class.temporary]/2.6: 该标准的最后一条规则适用于[class.temporary] /2.6:
Temporary objects are materialized [...]:
临时物品已实现[...]:
- when a prvalue appears as a discarded-value expression.
当prvalue显示为丢弃值表达式时。
Which produces the 6th temporaries. 这产生了第6个临时工。
Without RVO the return value is directly materialized, which makes temporary materialization of the return prvalues not necessary anymore. 如果没有RVO,则返回值将直接实现,这使得不再需要临时实现返回值。 This is why GCC produces the exact same assembly with and without
-fno-elide-constructors
compiler option. 这就是GCC使用和不使用
-fno-elide-constructors
编译器选项生成完全相同的程序集的原因。
In order to avoid temporary materialization, you could define operator +
: 为了避免临时实现,您可以定义
operator +
:
const Rational operator+(Rational lhs, const Rational& rhs)
{
return lhs += rhs;
}
With such a definition, the prvalue a+b
and (a+b)+c
would be directly used to initialize to first parameter of operator +
which would save you from the materialization of 2 temporaries. 通过这样的定义,prvalue
a+b
和(a+b)+c
将直接用于初始化为operator +
第一个参数,这将使您免于实现2个临时值。 See the assembly here . 在这里查看组件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.