C++編譯器的RVO和NRVO
阿新 • • 發佈:2021-10-20
1、說明
我一直記得返回物件的函式在呼叫時會有拷貝構造動作,但是最近實際測試卻和記憶有些偏差,經查詢是編譯的問題
RVO: return value optimization
NRVO: named return value optimization
這兩個是編譯器的一種函式返回值優化策略
先說結果,VS在debug模式下預設 RVO,release模式下預設 NRVO;而g++在debug和release下都預設NRVO
2、示例
先看一組程式碼
class Test { public: explicit Test(int num) : num(num) { cout << "constructor " << num << endl; } Test(const Test &test) { cout << "copy constructor " << test.num << endl; num = test.num; } ~Test() { cout << "destructor " << num << endl; } void print() const { cout << "print " << num << endl; } private: int num{}; }; Test getTest(int num) { Test test(num); return test; } int main() { Test test2 = getTest(12); test2.print(); return 0; }
函式 getTest() 返回一個物件,main() 函式中呼叫並複製給變數理應有一個拷貝構造的動作,但是實際上返回值為
constructor 12
print 12
destructor 12
列印變數地址也發現,getTest() 函式內的變數 test 和 main() 函式中的變數 test 的地址居然是一樣的。是我記錯了嗎?其實不是,根據C++語法,確實應該有拷貝構造的動作,這裡的結果是編譯器優化的後的,就是上文所說的 RVO 和 NRVO
3、編譯器優化
g++ 可以使用引數 -fno-elide-constructors 來關閉優化,CMakeList 使用以下程式碼關閉
add_compile_options(-fno-elide-constructors)
或者
set(CMAKE_CXX_FLAGS “-fno-elide-constructors ${CMAKE_CXX_FLAGS}”)
如果我們關閉編譯器的優化,最後輸出的結果應該是
constructor 12 //getTest()函式內構造test物件 copy constructor 12 //getTest()的返回值不能是test物件,需要一個臨時變數,使用test物件拷貝構造臨時物件_test destructor 12 //getTest()返回,test物件被析構 copy constructor 12 //main()函式使用test2變數接收臨時物件_test destructor 12 //臨時物件_test被析構 print 12 //print()呼叫 destructor 12 //main()函式內test2物件被析構
不難理解,我的記憶沒錯,根據C++語法確實應該有拷貝構造,只不過是編譯器優化了