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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Cpp / 通用引用、引用折叠与完美转发问题

發(fā)布時間:2024/10/14 编程问答 72 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Cpp / 通用引用、引用折叠与完美转发问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、通用引用

通用引用,允許其綁定右值(就像右值引用那樣)和左值(就像左值引用那樣)。而且,它們可以綁定 const 或者非 const 對象,可以綁定 volatile 和非 volatile 對象,還可以綁定 const 和 volatile 同時作用的對象。它們實際上可以綁定任何東西。構(gòu)成通用引用有兩個條件:

  • 必須精確滿足 T&& 這種形式(即使加上 const 也不行)

  • 類型T必須是通過推斷得到的(最常見的就是模板函數(shù)參數(shù))

二、引用折疊

引用折疊規(guī)則:

  • X& &,X& &&,X&& & 折疊為:X&
  • X&& && 折疊為:X&&

三、std::move 的工作原理

由以上知識,就可以解釋一下 std::move 的工作原理了:

template <typename T> typename remove_reference<T>::type &&move(T &&t) {return static_cast<typename remove_reference<T>::type &&>(t); }

首先,move 函數(shù)的參數(shù)類型是通用引用 T&&,可以綁定任意類型參數(shù),其次,返回值是 remove_reference<T> 的 type 成員類型的右值引用,比如 T 被推導(dǎo)為 int 或者 int&,則remove_reference<int&>::type 為 int 類型,返回值類型為 int&&,最后,函數(shù)體中 static_cast 內(nèi)的轉(zhuǎn)換過程類似,雖然不能隱式的將一個左值轉(zhuǎn)換為一個右值引用,但是通過 static_cast 顯式轉(zhuǎn)換時允許的(把左值截斷問題縮小在使用std::move代碼的范圍內(nèi))。

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

有時候,某些函數(shù)需要將其實參連同類型(const、左值、右值等屬性)不變的轉(zhuǎn)發(fā)給其他函數(shù)。

template <typename F, typename T> void sender(F receiver, T t) //sender函數(shù),接受一個可調(diào)用對象和一個模板參數(shù)類型的參數(shù) {receiver(t); //sender需要將自己的參數(shù)t轉(zhuǎn)發(fā)給receiver函數(shù) }

一般情況下這個函數(shù)能工作,但是當(dāng)它調(diào)用一個接受引用類型參數(shù)的函數(shù)時就會有問題:

void rec(int &i) { ++i; } int j = 1; rec(j); cout << j << endl; //輸出 j 為 2;//但是通過 sender 調(diào)用時: template <typename F, typename T> void sender(F receiver, T t) {receiver(t); } sender(rec, j); cout << j << endl; //輸出 j 為 1 。

其原因是 j 傳遞給 sender 函數(shù),推斷出 T 為 int 類型而非引用,j 的值是被拷貝到形參 t 中的,因此對 t 值的改變不會反應(yīng)到 j 中。

這時聯(lián)想到我們講的通用引用,將模板參數(shù)類型定義為 T&&,接受左值時,T 會被推斷為左值引用類型,經(jīng)過一次引用折疊,得參數(shù) t 的類型為左值引用,它對應(yīng)實參的 const 屬性和左值、右值屬性都將得到保持:

template <typename F, typename T> void sender(F receiver, T &&t) //通用引用 {receiver(t); }void rec(int &i) {++i; } sender(rec, j); cout << j << endl; //OK!輸出 j 為 2 。

但是這兒又會遇到另一個問題:

template <typename F, typename T> void sender(F receiver, T &&t) {receiver(t); }void rec(int &&i) //現(xiàn)在rec接受一個右值引用參數(shù) {++i; }int j = 1; sender(rec, j); //錯誤:無法從一個左值實例化int&& sender(rec, 1); //錯誤:無法從一個左值實例化int&&

當(dāng)我們試圖對一個接受右值引用的函數(shù)轉(zhuǎn)發(fā)參數(shù)時,會報以上錯誤,不論我們傳遞給 sender 函數(shù)的是一個左值還是右值。原因是傳遞給 rec 函數(shù)中形參 i 的是 sender 函數(shù)中的參數(shù) t,函數(shù)參數(shù)和其他變量一樣都是左值表達式!所以會出現(xiàn)將左值綁定到右值引用的錯誤。

這時需要用到 forward 函數(shù)來保證:當(dāng) sender 函數(shù)接受一個右值實參,轉(zhuǎn)發(fā)給 rec 函數(shù)時仍然能保持其右值屬性。forward 函數(shù)定義在 <utility> 頭文件中。

//forward函數(shù) //lvalue (1) template <typename Type> Type &&forward(typename remove_reference<Type>::type &arg) noexcept {return (static_cast<Type &&>(arg)); } //rvalue (2) template <typename Type> Type &&forward(typename remove_reference<Type>::type &&arg) noexcept {return (static_cast<Type &&>(arg)); }

forward 函數(shù)的工作原理:由 arg 接受的實參類型推斷出 Type 類型。
forward 函數(shù)必須通過顯式模板實參來調(diào)用,它跟通用引用配合可以保存原始實參的所有特性,回到我們的例子:

template <typename F, typename T> void sender(F receiver, T &&t) {receiver(std::forward<T>(t)); }

當(dāng)傳遞給 sender 的是一個右值——比如 10 時,推斷出來的 T 是一個普通類型即 int,傳給 forward 的形參 arg 的實參就是一個 int,從而推出 Type 是 int,這時調(diào)用 std::forward<int>(t) 返回的是 int&&,保存了原實參的右值屬性。

當(dāng)傳遞給 sender 的是一個左值——比如 j 時,這時我們推斷出來的 T 應(yīng)該是一個 int& 了,調(diào)用 std::forward<int&>(t) 返回 int & &&(無效代碼,演示)折疊成為 int&,保存了原實參的左值屬性。

?

轉(zhuǎn)載于:https://blog.csdn.net/qq_38216239/article/details/80815142

?

(SAW:Game Over!)

總結(jié)

以上是生活随笔為你收集整理的Cpp / 通用引用、引用折叠与完美转发问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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