C++11新特性--完美轉(zhuǎn)發(fā)

轉(zhuǎn)發(fā)的問題

????在模板編程中忱嘹,常有一種場景是把模板參數(shù)轉(zhuǎn)發(fā)給另一個函數(shù)調(diào)用疏之,這時候如果只提供值傳遞版本會顯得效率太低州弟∪月桑看一下代碼

template<class TYPE, class ARG>
TYPE* get_instance(ARG arg)
{
     TYPE* ret;
     ret = new TYPE(arg);
     return ret;
}

????代碼很簡單嘿悬,就是用ARG參數(shù)去初始化一個TYPE類型的對象,然后返回該對象指針水泉∩普牵考慮一下如果ARG類型是一個自定義類型,那么這樣的值傳遞會是比較大的性能開銷草则。有沒有辦法改進一下钢拧?看一下代碼

template<class TYPE, class ARG>
TYPE* get_instance(const ARG& arg)
{
     TYPE* ret;
     ret = new TYPE(arg);
     return ret;
}

????這段代碼將傳入改為了萬能的常量左值引用,可以接受任何類型炕横,可以解決性能開銷的問題源内,但是不夠靈活,如果我們想TYPE接受一個右值去初始化呢看锉?那么有沒有可以把參數(shù)連同類型一起轉(zhuǎn)發(fā)的方案呢姿锭?C++11提供了這樣能力既完美轉(zhuǎn)發(fā)。代碼如下

template<class TYPE, class ARG>
TYPE* get_instance(ARG&& arg)
{
     TYPE* ret;
     ret = new TYPE(std::forward<ARG>(arg));
     return ret;
}

形參改為了右值引用么伯铣?

兩個模板函數(shù)推導(dǎo)原則

????解釋一下前面留下的問題呻此。在模板函數(shù)中(注意是模板函數(shù)不是模板類,也不是普通函數(shù)腔寡,只是模板函數(shù))焚鲜,形如上例中的形參即T&&,如果T是一個推導(dǎo)類型(即模板參數(shù))它的意思并不是右值引用,它有一個專用的名字forwarding reference忿磅,它遵循以下的特殊推導(dǎo)規(guī)則來推導(dǎo)T的類型:
1糯彬、引用折疊原則(reference collapsing rule),下面以int為類型舉例葱她,以區(qū)別于模板參數(shù)T
a)int& & (引用的引用) 被轉(zhuǎn)化成 int&
b)int&& & (rvalue的引用)被傳化成 int&
c)int& && (引用作rvalue) 被轉(zhuǎn)化成 int&
d)int&& && 被轉(zhuǎn)化成 int&&
2撩扒、推導(dǎo)原則
以上面模板函數(shù)為例,三個特殊之處(左值引用吨些,常量左值引用如預(yù)期被推導(dǎo)出來)
a)如果傳入get_instance的參數(shù)是int類型(即左值)搓谆,那么ARG會被推導(dǎo)為int&,即ARG=int&
b)如果傳入get_instance的參數(shù)是右值int類型(即右值)豪墅,那么ARG被推導(dǎo)為int泉手,即ARG=int
c)如果傳入get_instance的參數(shù)是const int類型(即常量左值),那么ARG被推導(dǎo)為const int&偶器,即ARG=const int&

注意:為什么這里沒有參數(shù)類型是右值引用類型的參數(shù)斩萌??
其實右值引用是一個左值屏轰,即你傳入函數(shù)的實參類型有可能是一個左值(左值引用颊郎,左值,常量左值引用霎苗,右值引用)袭艺,也有可能是一個右值。注意到細微的區(qū)別了么叨粘?這里本章最后會給出一些代碼示例。

以上三個特殊推導(dǎo)之處

結(jié)合std::forward代碼來看一下

template <class _Tp>
inline
_Tp&&
forward(typename remove_reference<_Tp>::type& __t)
{
    return static_cast<_Tp&&>(__t);
}

template<class TYPE, class ARG>
TYPE* get_instance(ARG&& arg)
{
     TYPE* ret;
     ret = new TYPE(std::forward<ARG>(arg));
     return ret;
}

現(xiàn)在結(jié)合上面兩個原則來推導(dǎo)一下:
a)調(diào)用get_instance傳入int類型瘤睹,ARG推導(dǎo)為int&升敲,傳遞給std::forward的模板參數(shù)就是int&類型,std::forward中的typename remove_reference<_Tp>::type為int類型轰传,static_cast中_Tp&&類型根據(jù)引用折疊驴党,推導(dǎo)為int& &&即int&,最后forward轉(zhuǎn)發(fā)為int&類型获茬,完美轉(zhuǎn)發(fā)港庄。
b)調(diào)用get_instance傳入int右值類型,ARG推導(dǎo)為int恕曲,傳遞給std::forward的模板參數(shù)就是int類型鹏氧,std::forward中的typename remove_reference<_Tp>::type為int類型,static_cast中_Tp&&類型根據(jù)引用折疊佩谣,推導(dǎo)為int&&即int&&把还,最后forward轉(zhuǎn)發(fā)為int&&類型,完美轉(zhuǎn)發(fā)。
c)調(diào)用get_instance傳入const int類型吊履,ARG推導(dǎo)為const int&安皱,傳遞給std::forward的模板參數(shù)就是const int&類型,std::forward中的typename remove_reference<_Tp>::type為const int類型艇炎,static_cast中_Tp&&類型根據(jù)引用折疊酌伊,推導(dǎo)為const int& &&即const int&,最后forward轉(zhuǎn)發(fā)為const int&類型缀踪,完美轉(zhuǎn)發(fā)居砖。
d)調(diào)用get_instance傳入int&類型,ARG推導(dǎo)為int&辜贵,傳遞給std::forward的模板參數(shù)就是int&類型悯蝉,std::forward中的typename remove_reference<_Tp>::type為int類型,static_cast中_Tp&&類型根據(jù)引用折疊托慨,推導(dǎo)為int& &&即int&鼻由,最后forward轉(zhuǎn)發(fā)為int&類型,完美轉(zhuǎn)發(fā)厚棵。
e)調(diào)用get_instance傳入const int&類型蕉世,ARG推導(dǎo)為const int&,傳遞給std::forward的模板參數(shù)就是const int&類型婆硬,std::forward中的typename remove_reference<_Tp>::type為int類型狠轻,static_cast中_Tp&&類型根據(jù)引用折疊,推導(dǎo)為const int& &&即const int&彬犯,最后forward轉(zhuǎn)發(fā)為const int&類型向楼,完美轉(zhuǎn)發(fā)。

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

????上面分析的情況即為完美轉(zhuǎn)發(fā)谐区,總結(jié)一下是模板函數(shù)形參形如T&&湖蜕,結(jié)合庫函數(shù)std::forward來將參數(shù)實現(xiàn)類型的完美轉(zhuǎn)發(fā)。

代碼示例

????用代碼來補充說明一下上面留下的問題宋列。

template <typename T>
void
wrapper(T&& value)
{
    std::cout<<"T is a ref type(lvalue or rvalue ref)?:"<<std::is_reference<T>::value<<std::endl;
    std::cout<<"T is right value ref?:"<<std::is_rvalue_reference<T>::value<<std::endl; //none T is deduce to rvalue ref
}
static void execute()
{
    int left = 1;
    int &&right = 2;

    wrapper(right); //input is a lvalue
    wrapper(3); //intput is a rvalue
    wrapper(std::move(left)); //input is a rvalue
    wrapper(left); //input is a lvalue
}

輸出是
T is a ref type(lvalue or rvalue ref)?:1
T is right value ref?:0
T is a ref type(lvalue or rvalue ref)?:0
T is right value ref?:0
T is a ref type(lvalue or rvalue ref)?:0
T is right value ref?:0
T is a ref type(lvalue or rvalue ref)?:1
T is right value ref?:0

結(jié)果跟我們上述的分析是一致的昭抒,T不會推導(dǎo)為右值引用,原因是輸入?yún)?shù)只有左值和右值兩種炼杖,不存在右值引用這種輸入灭返。
簡單分析一下前三個wrapper調(diào)用的區(qū)別,第一句調(diào)用right是個右值引用類型的變量坤邪,自然可以被取地址熙含,所以它是一個左值。
第二句調(diào)用毋庸置疑傳入的是一個右值艇纺。
第三句調(diào)用跟第二句的效果一模一樣婆芦,由于std::move返回了一個右值引用類型的返回值怕磨,函數(shù)返回值通常是個右值,所以這里它的返回值自然是一個右值消约。

最后幾點值得注意的

????再對比一下完美轉(zhuǎn)發(fā)和接受一個右值引用的函數(shù)對比肠鲫。

template <typename T>
void
wrapper(T&& value);

void 
wrapper_none_template(int&& value);

????這兩個函數(shù)的區(qū)別是或粮,上面的模板函數(shù)即提現(xiàn)c++11所謂的完美轉(zhuǎn)發(fā)导饲,T會根據(jù)傳入實參參數(shù)的不同(左值、右值氯材、左值引用渣锦、常量左值引用)而推導(dǎo)出不同的類型。
下面的函數(shù)是一個只接受右值實參的函數(shù)氢哮,如果傳入的值不是右值袋毙,那么編譯會報錯。
????還有一點冗尤,用模板函數(shù)forwarding reference去進行轉(zhuǎn)發(fā)听盖,所有的傳入?yún)?shù)最終都是引用類型(左值的和右值的)即不產(chǎn)生值傳遞所造成的額外構(gòu)造和析構(gòu)。之所以提到這一點裂七,主要是針對傳入一個右值的情況皆看。前面提到如果傳入一個右值int,T會被推導(dǎo)為int背零,但是形參還是int&&腰吟,也就是說形參是一個右值引用,它綁定了一個右值徙瓶,所以還是一個引用毛雇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侦镇,隨后出現(xiàn)的幾起案子禾乘,更是在濱河造成了極大的恐慌,老刑警劉巖虽缕,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蒲稳,居然都是意外死亡氮趋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門江耀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剩胁,“玉大人,你說我怎么就攤上這事祥国£枪郏” “怎么了晾腔?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長啊犬。 經(jīng)常有香客問我灼擂,道長,這世上最難降的妖魔是什么觉至? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任剔应,我火速辦了婚禮,結(jié)果婚禮上语御,老公的妹妹穿的比我還像新娘峻贮。我一直安慰自己,他們只是感情好应闯,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布纤控。 她就那樣靜靜地躺著,像睡著了一般碉纺。 火紅的嫁衣襯著肌膚如雪船万。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天惜辑,我揣著相機與錄音唬涧,去河邊找鬼。 笑死盛撑,一個胖子當著我的面吹牛碎节,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抵卫,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狮荔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了介粘?” 一聲冷哼從身側(cè)響起殖氏,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎姻采,沒想到半個月后雅采,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡慨亲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年婚瓜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刑棵。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡巴刻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛉签,到底是詐尸還是另有隱情胡陪,我是刑警寧澤沥寥,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站柠座,受9級特大地震影響邑雅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜愚隧,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一蒂阱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狂塘,春花似錦录煤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泪漂,卻和暖如春廊营,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背萝勤。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工露筒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人敌卓。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓慎式,卻偏偏與公主長得像,于是被迫代替她去往敵國和親趟径。 傳聞我的和親對象是個殘疾皇子瘪吏,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容