亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

深入解讀C++中的右值引用

2020-05-23 14:03:24
字體:
來源:轉載
供稿:網友
這里來帶大家深入解讀C++中的右值引用,右值引用是C++新標準中的重要特性,包括C++11中的引用折疊,首先還是先來看一下右值引用的概念:
 

右值引用(及其支持的Move語意和完美轉發)是C++0x將要加入的最重大語言特性之一,這點從該特性的提案在C++ - State of the Evolution列表上高居榜首也可以看得出來。
從實踐角度講,它能夠完美解決C++中長久以來為人所詬病的臨時對象效率問題。從語言本身講,它健全了C++中的引用類型在左值右值方面的缺陷。從庫設計者的角度講,它給庫設計者又帶來了一把利器。從庫使用者的角度講,不動一兵一卒便可以獲得“免費的”效率提升…
在標準C++語言中,臨時量(術語為右值,因其出現在賦值表達式的右邊)可以被傳給函數,但只能被接受為const &類型。這樣函數便無法區分傳給const &的是真實的右值還是常規變量。而且,由于類型為const &,函數也無法改變所傳對象的值。C++0x將增加一種名為右值引用的新的引用類型,記作typename &&。這種類型可以被接受為非const值,從而允許改變其值。這種改變將允許某些對象創建轉移語義。比如,一個std::vector,就其內部實現而言,是一個C式數組的封裝。如果需要創建vector臨時量或者從函數中返回vector,那就只能通過創建一個新的vector并拷貝所有存于右值中的數據來存儲數據。之后這個臨時的vector則會被銷毀,同時刪除其包含的數據。有了右值引用,一個參數為指向某個vector的右值引用的std::vector的轉移構造器就能夠簡單地將該右值中C式數組的指針復制到新的vector,然后將該右值清空。這里沒有數組拷貝,并且銷毀被清空的右值也不會銷毀保存數據的內存。返回vector的函數現在只需要返回一個std::vector<>&&。如果vector沒有轉移構造器,那么結果會像以前一樣:用std::vector<> &參數調用它的拷貝構造器。如果vector確實具有轉移構造器,那么轉移構造器就會被調用,從而避免大量的內存分配。

一. 定義
通常意義上,在C++中,可取地址,有名字的即為左值。不可取地址,沒有名字的為右值。右值主要包括字面量,函數返回的臨時變量值,表達式臨時值等。右值引用即為對右值進行引用的類型,在C++98中的引用稱為左值引用。
如有以下類和函數:

class A{private: int* _p;};A ReturnValue(){ return A();}
ReturnValue()的返回值即為右值,它是一個不具名的臨時變量。在C++98中,只有常量左值引用才能引用這個值。
A& a = ReturnValue(); // error: non-const lvalue reference to type 'A' cannot bind to a temporary of type 'A'   const A& a2 = ReturnValue(); // ok
通過常量左值引用,可以延長ReturnValue()返回值的生命周期,但是不能修改它。C++11的右值引用出場了:
A&& a3 = ReturnValue();
右值引用通過”&&”來聲明, a3引用了ReturnValue()的返回值,延長了它的生命周期,并且可以對該臨時值進行修改。

二. 移動語義
右值引用可以引用并修改右值,但是通常情況下,修改一個臨時值是沒有意義的。然而在對臨時值進行拷貝時,我們可以通過右值引用來將臨時值內部的資源移為己用,從而避免了資源的拷貝:

#include<iostream>class A{public: A(int a) :_p(new int(a)) { } // 移動構造函數 移動語義 A(A&& rhs) : _p(rhs._p) { // 將臨時值資源置空 避免多次釋放 現在資源的歸屬權已經轉移 rhs._p = nullptr;  std::cout<<"Move Constructor"<<std::endl; } // 拷貝構造函數 復制語義 A(const A& rhs) : _p(new int(*rhs._p)) { std::cout<<"Copy Constructor"<<std::endl; } private: int* _p;};A ReturnValue() { return A(5); }int main(){ A a = ReturnValue(); return 0;}

運行該代碼,發現Move Constructor被調用(在g++中會對返回值進行優化,不會有任何輸出??梢酝ㄟ^-fno-elide-constructors關閉這個選項)。在用右值構造對象時,編譯器會調用A(A&& rhs)形式的移動構造函數,在移動構造函數中,你可以實現自己的移動語義,這里將臨時對象中_p指向內存直接移為己用,避免了資源拷貝。當資源非常大或構造非常耗時時,效率提升將非常明顯。如果A沒有定義移動構造函數,那么像在C++98中那樣,將調用拷貝構造函數,執行拷貝語義。移動不成,還可以拷貝。
std::move:
C++11提供一個函數std::move()來將一個左值強制轉化為右值:

A a1(5);A a2 = std::move(a1);
上面的代碼在構造a2時將會調用移動構造函數,并且a1的_p會被置空,因為資源已經被移動了。而a1的生命周期和作用域并沒有變,仍然要等到main函數結束后再析構,因此之后對a1的_p的訪問將導致運行錯誤。
std::move乍一看沒什么用。它主要用在兩個地方:
  • 幫助更好地實現移動語義
  • 實現完美轉發(下面會提到)

考慮如下代碼:

class B{public: B(B&& rhs) : _pb(rhs._pb) { // how can i move rhs._a to this->_a ? rhs._pb = nullptr; }private: A _a; int * pb;}
對于B的移動構造函數來說,由于rhs是右值,即將被釋放,因此我們不只希望將_pb的資源移動過來,還希望利用A類的移動構造函數,將A的資源也執行移動語義。然而問題出在如果我們直接在初始化列表中使用:_a(rhs._a) 將調用A的拷貝構造函數。因為參數 rhs._a 此時是一個具名值,并且可以取址。實際上,B的移動構造函數的參數rhs也是一個左值,因為它也具名,并且可取址。這是在C++11右值引用中讓人很迷惑的一點:可以接受右值的右值引用本身卻是個左值
這一點在后面的完美轉發還會提到?,F在我們可以用std::move來將rhs._a轉換為右值:_a(std::move(rhs._a)),這樣將調用A的移動構造。實現移動語義。當然這里我們確信rhs._a之后不會在使用,因為rhs即將被釋放。

三. 完美轉發
如果僅僅為了實現移動語義,右值引用是沒有必要被提出來的,因為我們在調用函數時,可以通過傳引用的方式來避免臨時值的生成,盡管代碼不是那么直觀,但效率比使用右值引用只高不低。
右值引用的另一個作用是完美轉發,完美轉發出現在泛型編程中,將模板函數參數傳遞給該函數調用的下一個模板函數。如:

template<typename T>void Forward(T t){ Do(t);}
上面的代碼中,我們希望Forward函數將傳入參數類型原封不動地傳遞給Do函數,即Forward函數接收的左值,則Do接收到左值,Forward接收到右值,Do也將得到右值。上面的代碼能夠正確轉發參數,但是是不完美的,因為Forward接收參數時執行了一次拷貝。
考慮到避免拷貝,我們可以傳遞引用,形如Forward(T& t),但是這種形式的Forward并不能接收右值作為參數,如Forward(5)。因為非常量左值不能綁定到右值。考慮常量左值引用:Forward(const T& t),這種形式的Forward能夠接收任何類型(常量左值引用是萬能引用),但是由于加上了常量修飾符,因此無法正確轉發非常量左值引用:
void Do(int& i){ // do something...}template<typename T>void Forward(const T& t){ Do(t);}int main(){ int a = 8; Forward(a); // error. 'void Do(int&)' : cannot convert argument 1 from 'const int' to 'int&' return 0;}

基于這種情況, 我們可以對Forward的參數進行const重載,即可正確傳遞左值引用。但是當Do函數參數為右值引用時,Forward(5)仍然不能正確傳遞,因為Forward中的參數都是左值引用。
下面介紹在 C++11 中的解決方案。
PS:引用折疊
C++11引入了引用折疊規則,結合右值引用來解決完美轉發問題:

typedef const int T;typedef T& TR;TR& v = 1; // 在C++11中 v的實際類型為 const int&
如上代碼中,發生了引用折疊,將TR展開,得到 T& & v = 1(注意這里不是右值引用)。 這里的 T& + & 被折疊為 T&。更為詳細的,根據TR的類型定義,以及v的聲明,發生的折疊規則如下:
T& + &  = T&T& + && = T&T&& + &  = T&T&& + && = T&&
上面的規則被簡化為:只要出現左值引用,規則總是優先折疊為左值引用。僅當出現兩個右值引用才會折疊為右值引用。
再談轉發
那么上面的引用折疊規則,對完美轉發有什么用呢?我們注意到,對于T&&類型,它和左值引用折疊為左值引用,和右值引用折疊為右值引用?;谶@種特性,我們可以用 T&& 作為我們的轉發函數模板參數:
template<typename T>void Forward(T&& t){ Do(static_cast<T&&>(t));}
這樣,無論Forward接收到的是左值,右值,常量,非常量,t都能保持為其正確類型。
當傳入左值引用 X& 時:
void Forward(X& && t){ Do(static_cast<X& &&>(t));}
折疊后:
void Forward(X& t){ Do(static_cast<X&>(t));}
這里的static_cast看起來似乎是沒有必要,而它實際上是為右值引用準備的:
void Forward(X&& && t){ Do(static_cast<X&& &&>(t));}
折疊后:
void Forward(X&& t){ Do(static_cast<X&&>(t));}
前面提到過,可以接收右值的右值引用本身卻是個左值,因為它具名并且可以取值。因此在Forward(X&& t)中,參數t已經是一個左值了,此時我們需要將其轉換為它本身傳入的類型,即為右值。由于static_cast中引用折疊的存在,我們總能還原參數本來的類型。
在C++11中,static_cast<T&&>(t) 可以通過 std::forward<T>(t) 來替代,std::forward是C++11用于實現完美轉發的一個函數,它和std::move一樣,都通過static_cast來實現。我們的Forward函數最終變成了:
template<typename T>void Forward(T&& t){ Do(std::forward<T>(t));}
可以通過如下代碼來測試:
#include<iostream>using namespace std;void Do(int& i)    { cout << "左值引用"  << endl; }void Do(int&& i)   { cout << "右值引用"  << endl; }void Do(const int& i) { cout << "常量左值引用" << endl; }void Do(const int&& i) { cout << "常量右值引用" << endl; }template<typename T>void PerfectForward(T&& t){ Do(forward<T>(t)); }int main(){ int a; const int b;  PerfectForward(a);  // 左值引用 PerfectForward(move(a)); // 右值引用 PerfectForward(b);  // 常量左值引用 PerfectForward(move(b)); // 常量右值引用 return 0;}

四. 附注
左值和左值引用,右值和右值引用都是同一個東西,引用不是一個新的類型,僅僅是一個別名。這一點對于理解模板推導很重要。對于以下兩個函數

template<typename T>void Fun(T t){ // do something...}template<typename T>void Fun(T& t){ // do otherthing...}

Fun(T t)和Fun(T& t)他們都能接受左值(引用),它們的區別在于對參數作不同的語義,前者執行拷貝語義,后者只是取個新的別名。因此調用Fun(a)編譯器會報錯,因為它不知道你要對a執行何種語義。另外,對于Fun(T t)來說,由于它執行拷貝語義,因此它還能接受右值。因此調用Fun(5)不會報錯,因為左值引用無法引用到右值,因此只有Fun(T t)能執行拷貝。
最后,附上VS中 std::move 和 std::forward 的源碼:

// movetemplate<class _Ty> inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT{  return ((typename remove_reference<_Ty>::type&&)_Arg);}// forwardtemplate<class _Ty> inline _Ty&& forward(typename remove_reference<_Ty>::type& _Arg){ // forward an lvalue return (static_cast<_Ty&&>(_Arg));}template<class _Ty> inline _Ty&& forward(typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT{ // forward anything static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call"); return (static_cast<_Ty&&>(_Arg));}


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久精品国产一区二区| www.欧美视频| 国产视频欧美视频| 亚洲午夜av电影| 日韩av资源在线播放| 亚洲第一页中文字幕| 欧美日韩免费在线| 中文字幕精品—区二区| 色诱女教师一区二区三区| 日韩欧美在线看| 亚洲国产精品yw在线观看| 国产精品久久久一区| 91色视频在线导航| 亚洲欧美日韩久久久久久| 国产婷婷97碰碰久久人人蜜臀| 欧美日韩性生活视频| 久久夜精品va视频免费观看| 欧美激情在线视频二区| 亚洲国产成人精品久久久国产成人一区| 91综合免费在线| 国产亚洲成av人片在线观看桃| 91精品国产自产在线| 欧美裸体xxxx| 国产亚洲免费的视频看| 国产精品成久久久久三级| 欧美性高跟鞋xxxxhd| 亚洲男人7777| 欧美大片va欧美在线播放| 福利二区91精品bt7086| 狠狠躁夜夜躁人人爽超碰91| 亚洲自拍偷拍在线| 91天堂在线观看| 精品视频偷偷看在线观看| 日本一欧美一欧美一亚洲视频| 亚洲精品资源美女情侣酒店| 91色中文字幕| 久久精品国产清自在天天线| 这里只有精品在线播放| 亚洲曰本av电影| 日韩av在线免费| 久久久精品一区| 91精品久久久久久久久青青| 狠狠干狠狠久久| 欧美精品在线免费| 国产精品r级在线| 国产亚洲精品美女久久久久| 亚洲精品一区二区在线| 亚洲视频专区在线| 国产精品成人免费视频| 久久久久久久成人| 亚洲成人久久久久| 欧美伦理91i| 久久精品夜夜夜夜夜久久| 欧美日韩国产一区在线| 8050国产精品久久久久久| 最近2019中文免费高清视频观看www99| 亚洲深夜福利在线| 超碰91人人草人人干| 黑人巨大精品欧美一区二区| 精品国产一区二区三区四区在线观看| 国产视频亚洲视频| 欧美成人午夜免费视在线看片| 性欧美激情精品| 欧洲亚洲妇女av| 色播久久人人爽人人爽人人片视av| 亚洲性生活视频| 日韩亚洲欧美中文高清在线| 亚洲已满18点击进入在线看片| 欧美疯狂做受xxxx高潮| 亚洲视频在线免费看| 欧美亚洲日本网站| 欧美激情区在线播放| 日韩精品视频在线免费观看| 欧美不卡视频一区发布| 亚洲精品国产精品乱码不99按摩| 国产一区二区三区在线看| 91av网站在线播放| 丝袜亚洲另类欧美重口| 国产精品亚洲综合天堂夜夜| 欧美性感美女h网站在线观看免费| 久久99青青精品免费观看| 欧美一级黑人aaaaaaa做受| 在线观看国产精品淫| 亚洲国产精品网站| 色爱av美腿丝袜综合粉嫩av| 日日骚久久av| 久久久久久美女| 国产亚洲精品91在线| 欧美激情第一页xxx| 欧美黄色性视频| 久久精品国产亚洲7777| 国产精品久久久久影院日本| 91国产精品视频在线| 岛国精品视频在线播放| 久久精品成人欧美大片古装| 欧美xxxx做受欧美| 日本电影亚洲天堂| 国产精品网站入口| 日韩精品视频免费| 中文字幕亚洲一区| 国产成人拍精品视频午夜网站| 日本国产欧美一区二区三区| 97涩涩爰在线观看亚洲| 91免费高清视频| 亚洲精品suv精品一区二区| 91久久久久久久| 色老头一区二区三区| 久久成人精品电影| 久久躁狠狠躁夜夜爽| 国产成人精品综合久久久| 亚洲热线99精品视频| 日韩精品一二三四区| 亚洲国产天堂网精品网站| 日韩亚洲欧美中文在线| 欧洲亚洲女同hd| 国产亚洲成精品久久| 国产成人在线精品| 91免费观看网站| 国产丝袜视频一区| 国产精品美女999| 最近2019年好看中文字幕视频| 97香蕉超级碰碰久久免费软件| 日韩二区三区在线| 亚洲第一页自拍| 中文字幕亚洲字幕| 欧美—级a级欧美特级ar全黄| 欧美性高跟鞋xxxxhd| 国产精品久久久久不卡| 2019日本中文字幕| 日韩精品在线观| 亚洲第一天堂无码专区| 国产91精品久久久| 亚洲最大成人在线| 曰本色欧美视频在线| 中文字幕日韩av电影| 亚洲精品久久久久久久久久久久久| 欧洲午夜精品久久久| 国产精品视频区| 日韩理论片久久| 国产成人精品视频在线观看| 中文字幕无线精品亚洲乱码一区| 中文字幕亚洲二区| 91精品国产综合久久香蕉的用户体验| 92看片淫黄大片欧美看国产片| 日韩av色综合| 欧美午夜影院在线视频| 久久久久久久久久婷婷| 国产成人短视频| 精品视频www| 国产精品夜间视频香蕉| 国产97在线|亚洲| 中文字幕国产精品| 亚洲视频国产视频| 91精品国产高清久久久久久| 久久久久久国产精品美女| 国产精品一区二区久久精品| 久久久亚洲影院你懂的| 久久久91精品国产一区不卡| 久久露脸国产精品| 日韩最新在线视频| 欧美在线视频网| 日韩av在线网| 亚洲人av在线影院| 欧美激情国产日韩精品一区18|