简体   繁体   English

在没有返回值优化的情况下将两个对象添加到一起时会创建多少个临时对象?

[英]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个临时值:

  1. One for materializing the result of a+b , 一个用于实现a+b的结果,
  2. the other for materializing the result of (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.

相关问题 从内联函数返回字符串时,会创建多少个临时对象? - How many temporary objects are created when I return a string from an inline function? 在这个初始化过程中创建了多少临时对象? - How many temporary objects are created in this initialization? 临时对象 - 何时创建,如何在代码中识别它们? - Temporary objects - when are they created, how do you recognise them in code? 什么时候对使用“new”创建的临时对象调用“delete”? - When is 'delete' called on temporary objects created with `new`? 如何在堆栈上临时创建对象的地址 - How are addresses of objects created on the stack temporary RVO(返回值优化)是否适用于所有对象? - Is RVO (Return Value Optimization) applicable for all objects? 如何创建临时对象以及实际执行的操作是什么? - How are temporary objects created and what's the actual operations that takes place? 有多少种生成临时对象/不必要调用构造函数的方法? - How many ways of generating temporary objects/needlessly invoking constructor are there? 对未命名对象的RVO(返回值优化)是否具有普遍保证的行为? - Is RVO (Return Value Optimization) on unnamed objects a universally guaranteed behavior? 为什么class Mat的两个对象可以在不重载运算符+的情况下相加? - Why can two objects of class Mat be added without overloading operator +?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM