[英]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.