日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

深入理解右值引用,move语义和完美转发

發(fā)布時(shí)間:2023/12/2 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解右值引用,move语义和完美转发 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

move語(yǔ)義

最原始的左值和右值定義可以追溯到C語(yǔ)言時(shí)代,左值是可以出現(xiàn)在賦值符的左邊和右邊,然而右值只能出現(xiàn)在賦值符的右邊。在C 里,這種方法作為初步判斷左值或右值還是可以的,但不只是那么準(zhǔn)確了。你要說(shuō)C 中的右值到底是什么,這真的很難給出一個(gè)確切的定義。你可以對(duì)某個(gè)值進(jìn)行取地址運(yùn)算,如果不能得到地址,那么可以認(rèn)為這是個(gè)右值。例如:

int& foo();foo() = 3; //ok, foo() is an lvalue int bar();int a = bar(); // ok, bar() is an rvalue

為什么要move語(yǔ)義呢?它可以讓你寫(xiě)出更高效的代碼。看下面代碼:

string?foo();string?name("jack");name?=?foo();

第三句賦值會(huì)調(diào)用string的賦值操作符函數(shù),發(fā)生了以下事情:

首先要銷(xiāo)毀name的字符串吧

把foo()返回的臨時(shí)字符串拷貝到name吧

最后還要銷(xiāo)毀foo()返回的臨時(shí)字符串吧

這就顯得很不高效,在C 11之前,你要些的高效點(diǎn),可以是swap交換資源。C 11的move語(yǔ)義就是要做這事,這時(shí)重載move賦值操作符

string&?string::operator=(string&&?rhs);

move語(yǔ)義不僅僅用于右值,也用于左值。標(biāo)準(zhǔn)庫(kù)提供了std::move方法,將左值轉(zhuǎn)換成右值。因此,對(duì)于swap函數(shù),我們可以這樣實(shí)現(xiàn):

templatevoid?swap(T&?a,?T&?b){????T?temp(std::move(a));????a?=?std::move(b);????b?=?std::move(temp);}

右值引用

string&& 這個(gè)類型就是所謂的右值引用,而把T&稱之為左值引用。注意,不要見(jiàn)到T&&就認(rèn)為是右值引用,例如,下面這個(gè)就不是右值引用:

T&&?foo?=?T();?//右值引用auto&&?bar?=?foo;?//?不是右值引用

實(shí)際上,T&&有兩種含義,一種就是常見(jiàn)的右值引用;另一種是即可以是右值引用,也可以是左值引用,Scott Meyers把這種稱為Universal Reference,后來(lái)C 委員把這個(gè)改成forwarding reference,畢竟forwarding reference只在某些特定上下文才出現(xiàn)。

有了右值引用,C 11增加了move構(gòu)造和move賦值。考慮這個(gè)情況:

void?foo(X&&?x){??//?...}

那么問(wèn)題來(lái)了,x的類型是右值引用,指向一個(gè)右值,但x本身是左值還是右值呢?

C 11對(duì)此做出了區(qū)分:

Things?that?are?declared?as?rvalue?reference?can?be?lvalues?or?rvalues.?The?distinguishing?criterion?is:?if?it?has?a?name,?then?it?is?an?lvalue.?Otherwise,?it?is?an?rvalue.

由此可知,x是個(gè)左值。考慮到派生類的move構(gòu)造,我們因這樣寫(xiě)才正確:

Derived(Derived&&?rhs):base(std::move(rhs)?//std::move不可缺{?...?}

有一點(diǎn)必須明白,那就是std::move不管接受的參數(shù)是lvalue,還是rvalue都返回rvalue。因此我們可以給出std::move的實(shí)現(xiàn)如下(很接近于標(biāo)準(zhǔn)實(shí)現(xiàn)):

template?typename?remove_reference::type&&?move(T&&?t)?{????using?RRefType?=?typename?remove_reference::type&&;????return?static_cast(t);}

完美轉(zhuǎn)發(fā)

假設(shè)有一個(gè)函數(shù)foo,我們寫(xiě)出如下函數(shù),把接受到的參數(shù)轉(zhuǎn)發(fā)給foo:

templatevoid?fwd(TYPE?t){????foo(t);}

我們一個(gè)個(gè)來(lái)分析:

如果TYPE是T的話,假設(shè)foo的參數(shù)引用類型,我會(huì)修改傳進(jìn)來(lái)的參數(shù),那么fwd(t)和foo(t)將導(dǎo)致不一樣的效果。

如果TYPE是T&的話,那么fwd傳一個(gè)右值進(jìn)來(lái),沒(méi)法接受,編譯出錯(cuò)。

如果TYPE是T&,而且重載個(gè)const T&來(lái)接受右值,看似可以,但如果多個(gè)參數(shù)呢,你得來(lái)個(gè)排列組合的重載,因此是不通用的做法。

你很難找到一個(gè)好方法來(lái)實(shí)現(xiàn)它,右值引用的引入解決了這個(gè)問(wèn)題,在這種上下文時(shí),它成為forwarding reference。這就涉及到兩條原則。第一條原則是引用折疊原則:

A&?&?折疊成?A&A&?&&?折疊成?A&A&&?&?折疊成?A&A&&?&&?折疊成?A&&

第二條是特殊模板參數(shù)推導(dǎo)原則:

1.如果fwd傳進(jìn)的是個(gè)A類型的左值,那么T被決議為A&。2.如果fwd傳進(jìn)的是個(gè)A類型的右值,那么T被決議為A。

將兩條原則結(jié)合起來(lái),就可以實(shí)現(xiàn)完美轉(zhuǎn)發(fā)。

A x; fwd(x); //推導(dǎo)出fwd(A& &&) 折疊后fwd(A&) A foo();fwd(foo());//推導(dǎo)出fwd(A&& &&) 折疊后 fwd(A&&)

std::forward應(yīng)用于forwarding reference,代碼看起來(lái)如下:

templatevoid?fwd(T&&?t){????foo(std::forward(t));}

要想展開(kāi)完美轉(zhuǎn)發(fā)的過(guò)程,我們必須寫(xiě)出forward的實(shí)現(xiàn)。接下來(lái)就嘗試forward該如何實(shí)現(xiàn),分析一下,std::forward是條件cast的,T的推導(dǎo)類型取決于傳參給t的是左值還是右值。因此,forward需要做的事情就是當(dāng)且僅當(dāng)右值傳給t時(shí),也就是當(dāng)T推導(dǎo)為非引用類型時(shí),forward需要將t(左值)轉(zhuǎn)成右值。forward可以如下實(shí)現(xiàn):

templateT&&?forward(typename?remove_reference::type&?t){????return?static_cast(t);}

現(xiàn)在來(lái)看看完美轉(zhuǎn)發(fā)是怎么工作的,我們預(yù)期當(dāng)傳進(jìn)fwd的參數(shù)是左值,從forward返回的是左值引用;傳進(jìn)的是右值,forward返回的是右值引用。假設(shè)傳給fwd是A類型的左值,那么T被推導(dǎo)為A&:

void?fwd(A&?&&?t){????foo(std::forward(t));}

forward實(shí)例化:

A&?&&?forward(typename?remove_reference::type&?t){????return?static_cast(t);}

引用折疊后:

A&?forward(A&?t){????return?static_cast(t);}

可見(jiàn),符合預(yù)期。再看看傳入fwd是右值時(shí),那么T被推導(dǎo)為A:

void?fwd(A?&&?t){????foo(std::forward(t));}

forward實(shí)例化如下:

A&&?forward(typename?remove_reference::type&?t){????return?static_cast(t);}

也就是:

A&&?forward(A&?t){????return?static_cast(t);}

forward返回右值引用,很好,完全符合預(yù)期。

總結(jié)

以上是生活随笔為你收集整理的深入理解右值引用,move语义和完美转发的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。