簡體   English   中英

為什么返回參數時不允許使用RVO?

[英]Why is RVO disallowed when returning a parameter?

它在[C ++ 11:12.8 / 31]中說明:

復制/移動操作的省略,稱為復制省略,允許[...]:

- 在具有類返回類型的函數的return語句中,當表達式是具有與函數返回類型相同的cv-unqualified類型的非易失性自動對象( 除函數或catch子句參數之外 )的名稱時,通過將自動對象直接構造到函數的返回值中,可以省略復制/移動操作

這意味着

#include <iostream>

using namespace std;

struct X
{
    X() { }
    X(const X& other) { cout << "X(const X& other)" << endl; }
};

X no_rvo(X x) {
    cout << "no_rvo" << endl;
    return x;
}

int main() {
    X x_orig;
    X x_copy = no_rvo(x_orig);

    return 0;
}

將打印

X(const X& other)
no_rvo
X(const X& other)

為什么需要第二個拷貝構造函數? 編譯器不能簡單地延長x的生命周期嗎?

想象一下no_rvo是在不同於main文件中定義的,因此在編譯main時編譯器只會看到聲明

X no_rvo(X x);

並且不知道返回的X類型的對象是否與參數有任何關系。 從它知道的那一點來看, no_rvo的實現也是如此

X no_rvo(X x) { X other; return other; }

所以當它例如編譯線時

X const& x = no_rvo(X());

當最大限度地優化時,它將執行以下操作。

  • 生成要傳遞給no_rvo的臨時X作為參數
  • 調用no_rvo ,並將其返回值綁定到x
  • 破壞它傳遞給no_rvo的臨時對象。

現在,如果no_rvo的返回值與傳遞給它的對象是同一個對象,那么臨時對象的銷毀將意味着銷毀返回的對象。 但這是錯誤的,因為返回的對象綁定到引用,因此將其生命周期延長到該語句之外。 然而,簡單地說不破壞參數也沒有解決方案,因為如果no_rvo的定義是我上面所示的替代實現那將是錯誤的。 因此,如果允許函數將參數重用為返回值,則可能會出現編譯器無法確定正確行為的情況。

請注意,對於常見的實現,編譯器無論如何都無法對其進行優化,因此它不是一個沒有正式允許的大損失。 另請注意,如果能夠證明這不會導致可觀察行為發生變化(即所謂的as-if規則), 允許編譯器優化副本。

RVO的通常實現是調用代碼傳遞內存塊的地址,其中函數應構造其結果對象。

當函數結果直接是一個不是形式參數的自動變量時,該局部變量可以簡單地放在調用者提供的內存塊中,然后return語句根本不復制。

對於通過值傳遞的參數,調用機器代碼必須在跳轉到函數之前將其實際參數初始化為形式參數的位置。 對於將結果放在那里的函數,它必須首先銷毀形式參數對象,這具有一些棘手的特殊情況(例如,當該構造直接或間接地引用形式參數對象時)。 因此,不是使用形式參數位置來標識結果位置,而是邏輯上必須使用單獨的調用提供的內存塊來實現函數結果。

然而,這不是在寄存器傳遞函數結果由呼叫者通常設置。 也就是說,人們可以合理地談論的是RVO,一種減少的RVO,對於表示正式參數的return表達式的情況,無論如何都會發生。 並且它不適合文本“通過將自動對象直接構造到函數的返回值”。

總而言之,要求調用者傳入值的數據流意味着調用者必須初始化正式參數的存儲,而不是函數。 因此,從形式參數拷貝回來,不能在一般的避免(即黃鼠狼詞涵蓋的特殊情況下,編譯器可以為內聯機器代碼做的很特別的東西,尤其是)。 但是,它是初始化任何其他本地自動對象的存儲的函數,然后執行RVO沒有問題。

暫無
暫無

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

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