就TM你叫std::forward啊岩瘦?

之前介紹過std::move未巫,今天我們就接著來說說std::forward。C++11引入了一個新特性:右值引用启昧,這個特性可以避免不必要的拷貝從而提高性能叙凡。

std::forward

我們先看看std::forward是干什么的,然后說說為什么需要它箫津。
根據(jù)前文狭姨,資源高效轉(zhuǎn)移的問題不是已經(jīng)有std::move來解決了么,為什么還需要另外一個std::forward來參和苏遥?注意“不必要”這個詞,既然有不必要赡模,那么就說明有時候是有必要的田炭。
雖然std::movestd::forward都和右值引用有關,但是側(cè)重點不同漓柑。std::move用在需要只右值引用的地方教硫;而std::forward用在一個需要統(tǒng)一引用(universal references)的地方叨吮,這個通用引用是什么?我更喜歡叫它薛定諤的引用瞬矩,因為它到底是左值引用還是右值引用是不確定的茶鉴,如果你給他傳遞左值它就是左值引用,如果給它傳個右值它就是右值引用景用。形如

 template<typename T>
T f(T&& param) {}

這種T&&就是通用引用涵叮。
假設我們有下面這么一個類,Foo伞插,在使用的過程中會出現(xiàn)以下兩種初始化方式:

class Foo {
 public:
   std::string member_;
   Foo(const std::string& member): member{member} {}

}
// Two use cases
// Case#1
std::string bar = "bar";
Foo foo(bar);
// Case#2
std::string bar = "bar";
Foo foo("foo" + bar);

這兩種方式有什么不同呢割粮?第一種使用場景中,我們已經(jīng)有了一個字符串"bar"和引用bar綁定在了一起媚污,我們希望用它來初始化Foo舀瓢,但是這個bar我們后續(xù)還需要使用,所以我們希望它拷貝一份給Foo耗美;第二種情況中京髓,"foo" + bar這個表達式生成了一個臨時字符串"foobar",由于它是臨時的商架,外部是沒有任何引用和他綁定的堰怨,很快就會被銷毀,因此我們希望能將它的內(nèi)存資源直接轉(zhuǎn)移給Foo而不是拷貝一份甸私。鑒于存在上述兩種使用場景诚些,常規(guī)情況下我們需要分別定義兩個構造函數(shù):

class Foo
{
public:
    std::string member;

    // Copy member.
    Foo(const std::string& member): member{member} {}

    // Move member.
    Foo(std::string&& member): member{std::move(member)} {}
};

但是我們懶,不想寫那么多構造函數(shù)皇型,有沒有辦法實現(xiàn)诬烹?有。我們使用std::forward:

class Foo
{
public:
    std::string member;

    template<typename T>
    Foo(T&& member): member{std::forward<T>(member)} {}
};

如果上面不夠清晰的話弃鸦,我們來看看下面這個例子:

#include <iostream>
#include <string>
#include <utility>

void foo(std::string& param) {
  std::cout << "std::string& version" << std::endl;
}
void foo(std::string&& param) {
  std::cout << "std::string&& version" << std::endl;
}

template<typename T>
void wrapper(T&& param) {
  // foo(param); 
  foo(std::forward<T>(param));
}

int main() {
  std::string foo("foo");
  wrapper(foo);
  wrapper(foo + "bar");
}

再上面的例子中绞吁,如果在wrapper中沒有使用std::forward,也就如果使用注釋掉的那個方法調(diào)用foo函數(shù)唬格,得到的結(jié)果將是這樣子:

std::string& version
std::string& version

而如果使用目前的方式調(diào)用foo家破,結(jié)果將是:

std::string& version
std::string&& version

std::forward到底做了什么?
它主要作用如下:根據(jù)模板參數(shù)T购岗,將模板函數(shù)的形參param變成在右值傳遞給函數(shù)foo或者將param保留為左值傳遞給函數(shù)foo汰聋。什么意思呢?就是如果傳遞個形參param的值是左值喊积,例如上面例子中的foo烹困,那么std::forward返回的是一個左值;果傳遞個形參param的值是右值乾吻,例如上面例子中的表達式foo + "bar"得到的是一個右值髓梅,那么std::forward返回的是一個右值拟蜻。因為根據(jù)C++語義,在函數(shù)wrapper的內(nèi)部枯饿,param是一個左值引用酝锅。
總的一句話就是std::forward能夠保留傳給形參param的實參的全部信息。wrapper(foo);中參數(shù)foo是左值奢方,那么wrapper傳給函數(shù)foo的就是左值搔扁;wrapper(foo + "bar");中參數(shù)foo + "bar"是右值,那么wrapper傳給函數(shù)foo的就是右值袱巨。

但是阁谆,std::forward是怎么知道一個形參的原本類型的呢?這里又引出兩個知識點:模板參數(shù)類型推導( template argument deduction)和引用則疊(Reference collapsing

引用則疊和模板參數(shù)類型推導

關于引用則疊和模板參數(shù)推斷愉老,可以說上一天场绿,所以這里步打算展開,僅僅簡單介紹下什么是引用則疊嫉入。假設有下面這種情況:

// T denotes the int& type
typedef int& T;
 
// TR is an lvalue reference to T
typedef T& TR;

// The declared type of var is TR
TR var; 

變量var的類型是TR焰盗,而TR是類型T的移用,T右是int類型的一個引用咒林,這樣一串下來熬拒,var的真實類型是什么呢?
引用的引用垫竞,不管是左值引用還是右值引用澎粟,在C++11之前是非法的,但是上面例子中的這種情況又是很可能出現(xiàn)的欢瞪。為了解決這一問題活烙,C++11定義了一套規(guī)則去處理引用的引用是什么的問題,這就是移用則疊遣鼓。
引用則疊主要右以下四條規(guī)則:

T TR Type of var
A& T& A&
A& T&& A&
A&& T& A&
A&& T&& A&&

套用到上面的小例子啸盏,var的類型就是一個int&。那這個到底和std::forward有什么關系呢骑祟?這里留下一個坑回懦,關于模板參數(shù)類型,以后有機會再說啦次企。

總結(jié)

std::forwardstd::move一樣怯晕,都與C++11引入的新特性右值引用相關。但是缸棵,與std::move不同的是贫贝,std::forward可以將參數(shù)保留它的類型信息,原樣轉(zhuǎn)發(fā)給下一個被調(diào)用的函數(shù)蛉谜。實現(xiàn)這一動作的原理是模板參數(shù)推導和引用則疊稚晚。

References

[1] ppreference.com
[2] Perfect Forwarding in C++11
[3] Reference collapsing (C++11)
[4] Advantages of using forward



這就是我的底線!型诚!歡迎掃碼或者微信搜索TensorBoy并關注獲取更多最新文章 , 學習使我快樂客燕!

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市狰贯,隨后出現(xiàn)的幾起案子也搓,更是在濱河造成了極大的恐慌,老刑警劉巖涵紊,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件傍妒,死亡現(xiàn)場離奇詭異,居然都是意外死亡摸柄,警方通過查閱死者的電腦和手機颤练,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驱负,“玉大人嗦玖,你說我怎么就攤上這事≡炯梗” “怎么了宇挫?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長酪术。 經(jīng)常有香客問我器瘪,道長,這世上最難降的妖魔是什么绘雁? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任橡疼,我火速辦了婚禮,結(jié)果婚禮上咧七,老公的妹妹穿的比我還像新娘衰齐。我一直安慰自己,他們只是感情好继阻,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布耻涛。 她就那樣靜靜地躺著,像睡著了一般瘟檩。 火紅的嫁衣襯著肌膚如雪抹缕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天墨辛,我揣著相機與錄音卓研,去河邊找鬼。 笑死,一個胖子當著我的面吹牛奏赘,可吹牛的內(nèi)容都是我干的寥闪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼磨淌,長吁一口氣:“原來是場噩夢啊……” “哼疲憋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起梁只,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缚柳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搪锣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秋忙,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年构舟,在試婚紗的時候發(fā)現(xiàn)自己被綠了灰追。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡旁壮,死狀恐怖监嗜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抡谐,我是刑警寧澤裁奇,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站麦撵,受9級特大地震影響刽肠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜免胃,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一音五、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧羔沙,春花似錦躺涝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诗充,卻和暖如春苍蔬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蝴蜓。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工碟绑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俺猿,地道東北人。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓格仲,卻偏偏與公主長得像押袍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子抓狭,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

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

  • C++右值引用 右值引用應該是C++11引入的一個非常重要的技術伯病,因為它是移動語義(Move semantics)...
    小白將閱讀 2,189評論 2 13
  • 下文先從C++11引入的幾個規(guī)則,如引用折疊否过、右值引用的特殊類型推斷規(guī)則、static_cast的擴展功能說起惭蟋,然...
    georgeguo閱讀 97,519評論 9 45
  • 在閱讀源碼的時候苗桂,我發(fā)現(xiàn)有兩個標準庫的方法被頻繁使用,他們就是std::move和std::forward告组,因此打...
    SunnyZhou1024閱讀 699評論 0 2
  • 本文原名《Rvalue Refernces, Move Semantics, and Perfect Forwar...
    金戈大王閱讀 4,167評論 2 6
  • 【所有相關命令的基本語法可以通過man查看煤伟,善用man】 第三章:基本的bash shell命令 1:關于bash...
    我自去留閱讀 853評論 0 0