簡體   English   中英

返回值優化的魔力是什么?

[英]What is the magic in return value optimization on this?

基本上,我正在做的事情如下。 我的 D 類有三個構造函數(默認、移動、復制)和兩個重載賦值運算符(移動和復制)。 我預計 D 類型對象的任何創建都會調用五個中的至少一個。

但是,如下創建 D 對象“d4”不會調用它們中的任何一個:

D d4( foo() );  // foo returns a D

這是重現我想知道的問題的代碼:

#include <iostream>
#include <vector>
#include <cassert>
using std::cout;
using std::endl;
    class D {
public:
    D()
    { cout << "D default"<<endl;}

    D(const D& d)
    {
        cout << "D copy" << endl;
    }

    D(D&& d)
    {
        cout << "D rval" << endl;
        assert(0);
    }

    D& operator=(D&& d)
    {
        cout << "D mv assign" << endl;
        return *this;
    }

    D& operator=(const D& d)
    {
        cout << "D copy assign" << endl;
        return *this;
    }

    volatile int v;
};

// return 
D foo()
{
    D res;
    cout <<"returning a non const D" << endl;
    return res;
}

int main()
{
    D d4(foo());

    return 0;
}

基本上,我假設 D(D&& d) 將被調用來創建 d4,因為 foo() 返回一個可能不會被采用的臨時地址。 實際上,只有當 -fno-elide-constructors 禁用返回值優化時,這才是正確的。

但是,如果未指定,則默認情況下即使在 -O0 上也會啟用 RV 優化。 然后,我所看到的如下:

D default
returning a non const D

我從標准輸出中看到的所有內容都來自 foo()。 創建 d4 本身並沒有給我什么。 這與我的預期不同。

我期待以下。 返回值的內存空間分配在調用者的堆棧中,而不是被調用者的堆棧中。 調用默認構造函數來觸摸內存空間。 不會發生從被調用者堆棧到調用者堆棧的復制。 之后,在調用者的堆棧中分配另一個內存空間。 由於返回值是一個右值,因此調用移動構造函數在“調用者堆棧中的另一個內存空間”上寫入一些內容。

我知道這可能需要冗余內存空間。 但是,特別是在我的示例中,移動構造函數將隨着 assert(0) 消失。 任何構造函數,但它會讓程序繼續。 結果,返回值優化在程序輸出方面產生了差異。

這是預期的嗎? 如果是,背后的原因是什么? 我已經測試了 g++-7.3.0 和 clang++-5.0.1。 他們是一樣的。

因為您使用 C++17,它承諾 RVO,即使您添加了 -O0。 這可能有幫助

我期待以下。 返回值的內存空間分配在調用者的堆棧中,而不是被調用者的堆棧中。 調用默認構造函數來觸摸內存空間。 不會發生從被調用者堆棧到調用者堆棧的復制。

好的,到目前為止還好。

之后,在調用者的堆棧中分配另一個內存空間。 由於返回值是一個右值,因此調用移動構造函數在“調用者堆棧中的另一個內存空間”上寫入一些內容。

啊,但在這里你假設錯了。 你看,RVO 不是唯一一種復制省略。 也可以省略從臨時返回值復制局部變量的初始化,而且確實如此。 因此,沒有“調用者堆棧中的另一個內存空間”,因為對象是直接構造到變量的內存位置的。

這是預期的嗎?

應該預料到會發生復制省略。 不應該期望復制省略會發生在你不能依賴復制/移動構造函數沒有副作用的意義上。

暫無
暫無

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

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