1. 程式人生 > 其它 >C++編譯器的RVO和NRVO

C++編譯器的RVO和NRVO

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++語法確實應該有拷貝構造,只不過是編譯器優化了