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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

一、通用引用

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

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

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

二、引用折疊

引用折疊規則:

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

四、完美轉發

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

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

一般情況下這個函數能工作,但是當它調用一個接受引用類型參數的函數時就會有問題:

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

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

這時聯想到我們講的通用引用,將模板參數類型定義為 T&&,接受左值時,T 會被推斷為左值引用類型,經過一次引用折疊,得參數 t 的類型為左值引用,它對應實參的 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) //現在rec接受一個右值引用參數 {++i; }int j = 1; sender(rec, j); //錯誤:無法從一個左值實例化int&& sender(rec, 1); //錯誤:無法從一個左值實例化int&&

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

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

//forward函數 //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 函數的工作原理:由 arg 接受的實參類型推斷出 Type 類型。
forward 函數必須通過顯式模板實參來調用,它跟通用引用配合可以保存原始實參的所有特性,回到我們的例子:

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

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

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

?

轉載于:https://blog.csdn.net/qq_38216239/article/details/80815142

?

(SAW:Game Over!)

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。