C++ 右值引用与左值引用
意義:可以避免無謂的復制,提高程序的性能。
左值:表達式結束后依然存在的持久化對象
右值:表達式結束后不再存在的臨時對象
所有的具名變量和對象都是左值,而右值不具名。
區分左值和右值的快捷方法:
看能不能對表達式取地址,如果能則是左值,否則就是右值。
右值分為純右值和將亡值。
純右值是C++98中的右值概念,如非引用函數返回的臨時變量;
一些運算表達式,如4+6產生的臨時變量;不和對象關聯的字面量值,
如10,‘s’,true,“hello”等這些不能被取地址的值。
將亡值:c++11中新增的和右值引用相關的表達式,這樣的表達式通常是將要移動的對象
T&&函數返回值,std::move()函數的返回值等。
將亡值和純右值統一看成右值,不影響使用。
c++98中引用很常見,就是給變量取一個別名,在c++11中,因為增加了右值引用的概念,
所以c++98中的引用都稱為左值引用。
c++11中的右值引用使用&&符號,如
int &&a = 1; int b = 1; int &&c = b; //編譯錯誤,不能將左值賦值給一個右值引用 class A { public:int a; }; A getTemp() {return A(); } A &&a = getTemp(); //getTemp()返回值是右值(臨時變量)getTemp()返回的右值本來在表達式語句結束后,其生命也就該終結了(臨時變量),而通過
右值引用,該右值又獲得了新生,其生命周期與右值引用類型變量a的生命一樣,只要a活著,該
右值臨時變量將會一直存活下去,實際上就是給臨時變量去了一個名字。
a的類型為右值引用類型(int &&),如果從左值和右值的角度區分它,它實際上是一個左值。
因為可以對它取地址,而且它還有名字,是一個已經命名的右值。
所以,左值引用只能綁定左值,右值引用只能綁定右值。常量左值引用是個特例,它可以算一個
萬能的引用類型,可以綁定非常量左值,常量左值,右值,而且在綁定右值的時候,可以像右值引用一樣
將右值的生命期延長,缺點是只能讀不能改。例子如下:
實際上,我們在很多情況下都使用了常量左值引用這個功能,例子如下:
class Copyable { public:Copyable() {}Copyable(const Copyable &o){std::cout << "Copied" << std::endl;} };Copyable ReturnRvalue() {return Copyable(); //返回一個臨時對象 }void AcceptVal(Copyable a) { } void AcceptRef(const Copyable &a) { }int main() {std::cout<<"pass by value"<<std::endl;AcceptVal(ReturnRvalue()); //應該調用2次拷貝構造函數std::cout<<"pass by reference"<<std::endl;AcceptRef(ReturnRvalue()); //應該只調用一次拷貝構造函數 }上述例子運行之后,結果和預想的不一樣。AcceptVal(ReturnRvalue())需要調用兩次拷貝構造函數,一次在ReturnRvalue()函數中,構造一個Copyable()對象,返回的時候會調用拷貝構造函數生成一個臨時對象。在調用AcceptVal()時,會將這個對象拷貝給函數的局部對象a,一共調用了兩次拷貝構造函數。而AcceptRef()的不同之處在于形參是常量左值引用,它能接收一個右值,而不需要拷貝。
實際的結果是,不管哪種方式,一次拷貝構造函數都沒有調用。
這是因為編譯器開啟了返回值優化(RVO/NRVO,RVO,Return Value Optimization返回值優化;NRVO,Nameed Return Valude Optimization)。編譯器發現ReturnRvalue內部生成了一個對象,返回之后還需要生成一個臨時對象調用拷貝構造函數,很麻煩,所以直接優化成一個對象,避免拷貝,而這個臨時變量又被賦值給了函數的形參,還是沒必要,這3個變量都用一個變量代替了,不需要調用拷貝構造函數。
為了能夠更好的觀測結果,可以在編譯的時候加上-fno-elide-constructors選項(關閉返回值優化),此時結果和預想的一樣。上述的例子是想說明常量左值可以綁定一個右值,可以減少一次拷貝(使用非常量左值引用會使失敗,因為ReturnRvalue()返回的是臨時對象(右值))。
//g++ test.cpp -o test -fno-elide-constructors總結:T是一個具體類型
(1)左值引用,使用T&,只能綁定左值
(2)右值引用,使用T&&,只能綁定右值
(3)常量左值,使用conts T&,可以綁定左值和右值。
(4)已命名的右值引用,編譯器會認為是左值。
(5)編譯器有返回值優化功能,但不可過于依賴。
參考:https://www.jianshu.com/p/d19fc8447eaa
總結
以上是生活随笔為你收集整理的C++ 右值引用与左值引用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国内WiFi模组厂商盘点及发展分析
- 下一篇: C/C++ 中memset()