亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

std :: forward如何工作?

std :: forward如何工作?

C++
嚕嚕噠 2019-11-22 12:35:01
我知道它的作用以及何時使用它,但是我仍然無法確定它是如何工作的。請盡可能詳細,并說明std::forward如果允許使用模板參數推導的話,什么時候不正確。我的部分困惑是:“如果有名稱,則是左值”-如果是這種情況,為什么std::forward在通過thing&& xvs 時行為會有所不同thing& x?
查看完整描述

3 回答

?
拉丁的傳說

TA貢獻1789條經驗 獲得超8個贊

首先,讓我們看一下std::forward根據標準執行的操作:


§20.2.3 [forward] p2


返回值: static_cast<T&&>(t)


(這T是顯式指定的模板參數,t它是傳遞的參數。)


現在,請記住參考折疊規則:


TR   R


T&   &  -> T&  // lvalue reference to cv TR -> lvalue reference to T

T&   && -> T&  // rvalue reference to cv TR -> TR (lvalue reference to T)

T&&  &  -> T&  // lvalue reference to cv TR -> lvalue reference to T

T&&  && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)

(從這個答案中偷偷偷走了。)


然后,讓我們看一下一個想要使用完美轉發的類:


template<class T>

struct some_struct{

  T _v;

  template<class U>

  some_struct(U&& v)

    : _v(static_cast<U&&>(v)) {} // perfect forwarding here

                                 // std::forward is just syntactic sugar for this

};

現在是一個示例調用:


int main(){

  some_struct<int> s1(5);

  // in ctor: '5' is rvalue (int&&), so 'U' is deduced as 'int', giving 'int&&'

  // ctor after deduction: 'some_struct(int&& v)' ('U' == 'int')

  // with rvalue reference 'v' bound to rvalue '5'

  // now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int&&>(v)'

  // this just turns 'v' back into an rvalue

  // (named rvalue references, 'v' in this case, are lvalues)

  // huzzah, we forwarded an rvalue to the constructor of '_v'!


  // attention, real magic happens here

  int i = 5;

  some_struct<int> s2(i);

  // in ctor: 'i' is an lvalue ('int&'), so 'U' is deduced as 'int&', giving 'int& &&'

  // applying the reference collapsing rules yields 'int&' (& + && -> &)

  // ctor after deduction and collapsing: 'some_struct(int& v)' ('U' == 'int&')

  // with lvalue reference 'v' bound to lvalue 'i'

  // now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int& &&>(v)'

  // after collapsing rules: 'static_cast<int&>(v)'

  // this is a no-op, 'v' is already 'int&'

  // huzzah, we forwarded an lvalue to the constructor of '_v'!

}

我希望這個循序漸進的答案可以幫助您和其他人了解其std::forward工作原理。


查看完整回答
反對 回復 2019-11-22
?
繁星點點滴滴

TA貢獻1803條經驗 獲得超3個贊

我認為std::forwardas 的解釋static_cast<T&&>令人困惑。我們對轉換的直覺是將類型轉換為其他類型-在這種情況下,它將是對右值引用的轉換。不是!因此,我們正在使用另一種神秘事物來解釋一種神秘事物。這種特殊的演員表由Xeo的答案中的表格定義。但是問題是:為什么?所以這是我的理解:


假設我想向您傳遞一個std::vector<T> v您應該作為數據成員存儲在數據結構中的數據_v。天真的(和安全的)解決方案是始終將向量復制到其最終目的地。因此,如果通過中間函數(方法)執行此操作,則應將該函數聲明為引用。(如果將其聲明為按值獲取向量,則將執行其他完全不必要的復制。)


void set(const std::vector<T> & v) { _v = v; }

如果您手里有一個左值,這一切都很好,但是右值呢?假設向量是調用函數的結果makeAndFillVector()。如果您執行直接分配:


_v = makeAndFillVector();

編譯器將移動向量而不是復制向量。但是,如果您引入中介,set()則有關您的論據右值性質的信息將丟失,并將進行復制。


set(makeAndFillVector()); // set will still make a copy

為了避免這種復制,您需要“完美轉發”,這將每次都能獲得最佳代碼。如果給定左值,則希望函數將其視為左值并進行復制。如果給定了右值,則希望函數將其視為右值并將其移動。


通常,您可以通過set()分別為lvalues和rvalues 重載函數來做到這一點:


set(const std::vector<T> & lv) { _v = v; }

set(std::vector<T> && rv) { _v = std::move(rv); }

但是現在想象一下,您正在編寫一個接受T和調用set()該函數的模板函數T(不必擔心我們set()僅為向量定義的事實)。訣竅在于,set()當模板函數使用左值實例化時,您希望此模板調用第一個版本,而當使用右值初始化時,則希望該模板調用第二個版本。


首先,該函數的簽名應該是什么?答案是這樣的:


template<class T>

void perfectSet(T && t);

根據調用此模板函數的方式,T將在某種程度上以不同的方式神奇地推斷出類型。如果使用左值調用它:


std::vector<T> v;

perfectSet(v);

向量v將通過引用傳遞。但是,如果使用右值調用它:


perfectSet(makeAndFillVector());

(匿名)向量將通過右值引用傳遞。因此,故意設置C ++ 11魔術以盡可能保留參數的右值性質。


現在,在perfectSet內部,您希望將參數完美地傳遞給的正確重載set()。這std::forward是必需的:


template<class T>

void perfectSet(T && t) {

    set(std::forward<T>(t));

}

如果沒有std :: forward,編譯器將不得不假定我們要通過引用傳遞t。為了使自己確信這是真的,請比較以下代碼:


void perfectSet(T && t) {

    set(t);

    set(t); // t still unchanged

}

對此:


void perfectSet(T && t) {

    set(std::forward<T>(t));

    set(t); // t is now empty

}

如果您未明確轉發t,則編譯器必須防御性地假設您可能會再次訪問t并選擇set的左值引用版本。但是,如果轉發t,則編譯器將保留其右值性,并且set()將調用的右值引用版本。此版本移動的內容t,這意味著原始內容為空。


這個答案比我最初認為的要長得多;-)


查看完整回答
反對 回復 2019-11-22
  • 3 回答
  • 0 關注
  • 650 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號