簡體   English   中英

在沒有返回值優化的情況下將兩個對象添加到一起時會創建多少個臨時對象?

[英]How many temporary objects are created when two objects are added together without the return value optimization?

在閱讀Scott Meyers所着的“更有效的C ++”一書的第20和22項后,我決定提出這個問題。

假設你寫了一個代表有理數的類:

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
    ...
};

現在讓我們假設您決定使用operator+=實現operator+

const Rational operator+(const Rational& lhs, const Rational& rhs)
{
    return Rational(lhs) += rhs;
}

我的問題是:如果禁用了返回值優化operator+將創建多少個臨時變量?

Rational result, a, b;
...
result = a + b;

我相信創建了2個臨時值:一個是在operator+的主體內執行Rational(lhs) ,另一個是在operator+返回的值是通過復制第一個臨時值來創建的。

當Scott提出這個操作時,我的困惑出現了:

Rational result, a, b, c, d;
...
result = a + b + c + d;

並寫道:“可能使用3個臨時對象,每個調用一個operator+ ”。 我相信如果禁用返回值優化 ,上面的操作將使用6個臨時對象(每次調用operator+ ),而如果啟用,則上面的操作根本不使用臨時值。 斯科特是如何得出他的結果的? 我認為這樣做的唯一方法是部分應用返回值優化

我認為你只是在考慮太多,尤其是優化的細節。

result = a + b + c + d; ,作者只想說明將創建3個臨時值,第1個是a + b的結果,第2個是temporary#1 + c的結果,第3個是temporary#2 + d然后它被分配給result 在那之后,3個臨時工被摧毀。 所有臨時工作僅用作中間結果。

另一方面,像表達模板這樣的一些習語可以直接通過消除臨時性來獲得最終結果。

編譯器可以檢測累積並應用優化,但通常從左到右移位和減少表達式在某種程度上是棘手的,因為它可能被樣式a + b * c * d的表達式擊中

采取形式更謹慎:

a +(b +(c + d))

在優先級較高的運營商可能需要之前,它不會消耗變量。 但是評估它需要時間。

編譯器不會創建任何變量。 因為變量是出現在源代碼中的變量 ,並且變量在執行時或可執行文件中不存在 (它們可能成為內存位置,或者被“忽略”)。

閱讀as-if規則 編譯器經常進行優化

請參閱CppCon 2017 Matt Godbolt“我的編譯器最近為我做了什么? 解開編譯器的蓋子“談話

在表達式a+b+c+d 6中將創建和銷毀臨時數,這是強制性的(有和沒有RVO)。 你可以在這里查看

operator + definition中,在表達式Rational(lhs)+=a ,prvalue Rational(lhs)將綁定到operator+=隱含對象參數 ,該參數根據這個非常具體的規則[over.match.func] /授權5.1 (參見[expr.call] / 4

即使隱式對象參數不是const限定的,也可以將rvalue綁定到參數,只要在所有其他方面,參數可以轉換為隱式對象參數的類型。

然后將prvalue綁定到引用,必須進行臨時實現[class.temporary] /2.1

臨時物品已實現[...]:

  • 將引用綁定到prvalue時

因此,在每個operator +呼叫的執行期間創建臨時。

然后表達式Rational(lhs)+=a一次返回可以概念性地看作Rational(Rational(lhs)+=a)是一個prvalue( prvalue是一個表達式,其評估初始化一個對象 - phi:一個權力對象)然后綁定到后續2次調用operator +的第一個參數。 引用的規則[class.temporary] /2.1再次應用兩次,將創建2個臨時值:

  1. 一個用於實現a+b的結果,
  2. 另一個用於實現(a+b)+c

所以在這一點上已經創造了4個臨時工。 然后,第三次調用operator+在函數體內創建第5個臨時值

最后一次調用operator +丟棄的值表達式 該標准的最后一條規則適用於[class.temporary] /2.6:

臨時物品已實現[...]:

  • 當prvalue顯示為丟棄值表達式時。

這產生了第6個臨時工。

如果沒有RVO,則返回值將直接實現,這使得不再需要臨時實現返回值。 這就是GCC使用和不使用-fno-elide-constructors編譯器選項生成完全相同的程序集的原因。

為了避免臨時實現,您可以定義operator +

const Rational operator+(Rational lhs, const Rational& rhs)
{
    return lhs += rhs;
}

通過這樣的定義,prvalue a+b(a+b)+c將直接用於初始化為operator +第一個參數,這將使您免於實現2個臨時值。 這里查看組件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM