已知下列函數定義:
X bar() {
X xx;
// process xx ...
return xx;
}
你可能會問bar()的返回值如何從局部對象xx中拷貝過來?
實現模型1
返回值的初始化(Return Value Initialization)
Stroustrup在cfront中的解決做法是一個雙階段轉化:
- 首先加上一個額外參數,參數類型是對對象的引用,用來存放返回結果;
- 在return指令之前安插一個copy constructor調用操作,對這個參數利用返回值進行拷貝初始化。
根據上述算法,bar()的轉換如下:
// function transformation to reflect
// application of copy constructor
// Pseudo C++ Code
void bar( X& __result )
{
X xx;
// compiler generated invocation
// of default constructor
xx.X::X();
// ... process xx
// compiler generated invocation
// of copy constructor
__result.X::X( xx );
return;
}
實現模型2
在編譯器層面做優化(Optimization at the Compiler Level)
Named Return Value (NRV) optimization,具名返回值優化。
在一個像bar()這樣的函數中,所有的return指令傳回相同的具名數值,因此編譯器有可能自己做優化,方法是:以result參數取代named return value。
根據這個方法,bar()的轉換如下:
//__result is substituted for xx by the compiler:
void bar( X &__result )
{
// default constructor invocation
// Pseudo C++ Code
__result.X::X();
// ... process in __result directly
return;
}
Named Return value 優化:
NRV優化的本質是優化掉拷貝構造函數。在實現模型1中我們看到了,返回對象的實現總是先對某個對象進行操作,操作完成後,使用Copy Constructor將操作後的對象內容複製到另外一個對象中,然後返回。 基於這樣一個前提,NRV可以調用Copy Constructor這一步被省掉。所以NRV的前提必須要有Copy Constructor。
NRV也有副作用,就是Copy Constructor中的代碼不會被執行。
【注意】一方面要考慮默認的語義是否符合我們的需要。另一方面如果對象面臨大量的拷貝操作[ 比如這個class的object需要經常以傳值的方式返回],有必要實現一個拷貝構造函數以支持NRV優化。但是如果想使用底層的memcpy之類的直接進行bit wise copy,注意是否真的是bit wise copy拷貝,比如如果是virtual,這樣可能破壞調vptr。