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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Cpp / std::move 原理

發布時間:2024/10/14 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Cpp / std::move 原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

零、功能和源碼

std::move 是一個類型轉換器,將左值轉換成右值,其實現如下:

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

std::move 的實現還是挺簡單的就這么幾行代碼,但要理解這幾行代碼可不容易。下面我們就來對它做下詳細分析。

一、通用引用

首先我們來看一下 move 的輸入參數,move 的輸入參數類型稱為通用引用類型。什么是通用引用呢?就是它既可以接收左值也可以接收右值。我們來看一下例子:

#include <iostream>template <typename T> void func(T &&param) {std::cout << "the value is " << param << std::endl;return; }int main(int argc, char *argv[]) {int a = 123;auto &&b = 5; //通用引用,可以接收右值//int &&c = a; //錯誤,右值引用,不能接收左值auto &&d = a; //通用引用,可以接收左值//const auto &&e = a; //錯誤,加了const就不再是通用引用了func(a); //通用引用,可以接收左值func(10); //通用引用,可以接收右值return 0; }

通用引用成立條件:一種是 auto,另一種是通過模板定義的 T&&。實際上 auto 就是模板中的 T,它們是等價的。

下面我們就對這段代碼做下詳細解讀。 代碼中的 a 是個左值,因為它在內存中會分配空間,這應該沒什么異義;b 是通過引用。為什么呢?因為通用引用有兩個條件:一,必須是 T&& 的形式,由于 auto 等價于T,所以 auto && 符合這個要求;二,T 類型要可以推導,也就是說它必須是個模板,而 auto 是模板的一種變型,因此 b 是通用引用。通用引用即可以接收左值,也可以接收右值,所以 b = 5 是正確的;c 不是通用引用,因為它不符合 T&& 的形式。所經第三行代碼是錯誤的,右值引用只能接收右值;d 是通用引用,所以給它賦值 a 是正確的;e 不是通用引用,它多了一個 const 已不符合 T&& 的形式,所以給它左值肯定會出錯;最后兩個函數調用的形參符合 T&&,又因是模板可以進行類型推導,所以是通用引用,因此給它傳左值和右值它都能正確接收。

二、模板的類型推導

通用引用好強大呀!它既可以接收左值又可以接收右值,它是如何做到的呢?這就要講講模板的類型推導了。

模板的類型推導規則還是蠻復雜的,這里我們只簡要說明一下,有興趣的同學可以查一下 C++11 的規范。我們還是舉個具體的例子吧:

template <typename T> void func(ParamType param);func(expr);

上面這個例子是函數模板的通用例子,其中 T 是根據?func 函數的參數推到出來的,而 ParamType 則是根據 T 推導出來的。T 與 ParamType 有可能相等,也可能不等,因為 ParamType 是可以加修飾的。我們看下面的例子:

template <typename T> void f(T param);template <typename T> void func(T &param);template <typename T> void function(T &&param);int main(int argc, char *argv[]) {int x = 10; // x 是 intint &rr = x; // rr 是 int &const int cx = x; // cx 是 const intconst int &rx = x; // rx 是 const int &int *pp = &x; // pp 是 int *//下面是傳值的模板,由于傳入參數的值不影響原值,所以參數類型退化為原始類型f(x); // T 是 intf(cx); // T 是 intf(rx); // T 是 intf(rr); // T 是 intf(pp); // T 是 int*,指針比較特殊,直接使用//下面是傳引用模板, 如果輸入參數類型有引用,則去掉引用;如果沒有引用,則輸入參數類型就是 T 的類型func(x); // T 為 intfunc(cx); // T 為 const intfunc(rx); // T 為 const intfunc(rr); // T 為 intfunc(pp); // T 是 int*,指針比較特殊,直接使用//下面是通用引用模板,與引用模板規則一致function(x); // T 為 int&function(5); // T 為 int }

上面代碼中可以將類型推導分成兩大類:其中類型不是引用也不是指針的模板為一類; 引用和指針模板為另一類。

對于第一類其推導時根據的原則是,函數參數傳值不影響原值,所以無論你實際傳入的參數是普通變量、常量還是引用,它最終都退化為不帶任何修修飾的原始類型。如上面的例子中,const int &類型傳進去后,退化為 int 型了。

第二類為模板類型為引用(包括左值引用和右值引用)或指針模板。這一類在類型推導時根據的原則是去除對等數量的引用符號,其它關鍵字照般。還是我們上面的例子,func(x)中 x 的類型為 int&,它與 T& 放在一起可以知道 T 為 int。另一個例子 function(x),其中 x 為 int& 它與 T&& 放在一起可知 T 為 int&。

根據推導原則,我們可以知道通用引用最終的結果是什么了,左值與通用引用放在一起推導出來的 T 仍為左值,而右值與通用引用放在一起推導出來的 T 仍然為右值。

三、move 的返回類型

實際上上面通過模板推導出的 T 與 move 的返回類型息息相關的,要講明白這一點我們先要把 move 的返回類型弄明白。下面我們就來討論一下 move 的返回類型:

typename remove_reference<T>::type&&

move 的返回類型非常奇特,我們在開發時很少會這樣寫,它表示的是什么意思呢?

這就要提到 C++ 的另外一個知識點,即類型成員。你應該知道 C++ 的類成員有成員函數、成員變量、靜態成員三種類型,但從 C++11 之后又增加了一種成員稱為類型成員。類型成員與靜態成員一樣,它們都屬于類而不屬于對象,訪問它時也與訪問靜態成員一樣用::訪問。

了解了這點,我們再看 move 的返類型是不是也不難理解了呢?它表達的意思是返回 remove_reference 類的 type 類型成員。而該類是一個模板類,所以在它前面要加 typename 關鍵字。

remove_reference 看著很陌生,接下來我們再分析一下 remove_reference 類,看它又起什么作用吧。其實,通過它的名子你應該也能猜個大概了,就是通過模板去除引用。我們來看一下它的實現吧。

template <typename T> struct remove_reference {typedef T type; //定義T的類型別名為type };template <typename T> struct remove_reference<T &> //左值引用 {typedef T type; }template <typename T> struct remove_reference<T &&> //右值引用 {typedef T type; }

上面的代碼就是 remove_reference 類的代碼,在 C++ 中 struct 與 class 基本是相同的,不同點是 class 默認成員是 private,而 struct 默認是 public,所以使用 struct 代碼會寫的更簡潔一些。

通過上面的代碼我們可以知道,經過 remove_reference 處理后,T 的引用被剔除了。假設前面我們通過 move 的類型自動推導得到 T 為 int&&,那么再次經過模板推導 remove_reference 的 type 成員,這樣就可以得出 type 的類型為 int 了。

remove_reference 利用模板的自動推導獲取到了實參去引用后的類型?,F在我們再回過來看 move 函數的時候是不是就一目了解了呢?之前無法理解的 5 行代碼現然變成了這樣:

int &&move(int &&&&t) {return static_cast<int &&>(t); } //或 int &&move(int &&&t) {return static_cast<int &&>(t); }

經上面轉換后,我們看這個代碼就清晰多了,從中我們可以看到move實際上就是做了一個類型的強制轉換。如果你是左值引用就強制轉換成右值引用。

四、引用折疊

上面的代碼我們看起來是簡單了很多,但其參數 int& && 和 int && && 還是讓人覺得很別扭。因為 C++ 編譯器根本就不支持這兩種類型。咦!這是怎么回事兒呢?

到這里我們就要講到最后一個知識點引用折疊了。在C++中根本就不存 int& &&、int && && 這樣的語法,但在編譯器內部是能將它們識別出來的。換句話說,編譯器內部能識別這種格式,但它沒有給我們提供相應的接口(語法)。

實際上,當編譯器遇到這類形式的時候它會使用引用折疊技術,將它們變成我們熟悉的格式。其規則如下:

  • int & & 折疊為 int&
  • int & && 折疊為 int&
  • int && & 折疊為 int&
  • int && && 折疊為 int &&

總結一句話就是左值引用總是折疊為左值引用,右值引用總是折疊為右值引用。

經過這一系列的操作之后,對于一個具體的參數類型int & a,std::move 就變成了下面的樣子:

int &&move(int &t) {return static_cast<int &&>(t); }

這一下我們就清楚它在做什么事兒了哈!

?

轉載于:http://avdancedu.com/a39d51f9/

?

(SAW:Game Over!)

總結

以上是生活随笔為你收集整理的Cpp / std::move 原理的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 少妇与公做了夜伦理69 | 国产男人天堂 | 欧美双性人妖o0 | 影音先锋久久 | 久久久久99精品成人片直播 | 国产精品国产一区二区三区四区 | 色网导航站 | 欧美精品黑人猛交高潮 | 国产又色又爽又黄又免费 | 999久久久久 | 国产高潮视频在线观看 | 欧洲一区二区视频 | 先锋影音男人 | 午夜在线观看视频网站 | www.99爱| 黄色网战在线观看 | 国产一区二区三区精品在线 | 瑟瑟在线视频 | 老司机深夜福利网站 | 欧美精品免费一区二区三区 | xxxx性视频| 色窝窝无码一区二区三区成人网站 | 亚洲free性xxxx护士白浆 | 亚洲欧美成人 | 九九热这里只有精品6 | 猛1被调教成公厕尿便失禁网站 | ktv做爰视频一区二区 | 伊人网址 | 亚洲一区二区免费视频 | 一本久久综合 | 韩国三级国产 | 亚洲av无码专区在线电影 | 91精品国产一区二区在线观看 | 亚洲一区二区三区乱码 | 美女被娇喘视频 | wwwxxx在线播放 | 四虎毛片| 亚洲va久久久噜噜噜久久天堂 | 天堂在线精品 | 欧美在线亚洲 | 免费av电影网址 | 97超碰福利 | 亚洲精品网站在线播放gif | 久久久久久久久久久久久女国产乱 | av男人天堂网 | a免费毛片 | 久久久久久亚洲精品 | 伊人久久激情 | 佐山爱在线视频 | 欧美人妻精品一区二区三区 | 天天爽天天爽天天爽 | 欧美人体一区二区 | 日本一级一片免费视频 | 91桃色视频在线观看 | 国产黄色小视频在线观看 | 色妇网 | 亚欧美在线 | 成人三级做爰av | 国语对白做受69按摩 | 国产成人+综合亚洲+天堂 | 成人免费在线小视频 | 日韩欧美一区二区区 | av毛片在线免费观看 | 日韩成人短视频 | 日韩精品手机在线 | 国产 第1190页 | 日本高清久久 | jizz日本少妇 | 亚洲 小说 欧美 激情 另类 | 日韩乱码人妻无码系列中文字幕 | 亚洲人人精品 | 国产日韩欧美视频在线观看 | 三上悠亚亚洲一区 | 亚洲精品久久久久久 | 亚洲乱码国产乱码精品天美传媒 | 免费的毛片视频 | 国产视频一区二区 | 天天做夜夜爱 | 在线观看视频毛片 | 99人妻少妇精品视频一区 | 国产精品一区二区在线播放 | 一本大道伊人av久久综合 | 巨胸喷奶水www久久久免费动漫 | 中文字幕日韩亚洲 | 少妇精品久久久一区二区三区 | 亚洲综合免费观看高清完整版 | 日韩毛片中文字幕 | 蜜臀99久久精品久久久久久软件 | 国产xxxx孕妇 | 欧美精品欧美极品欧美激情 | 在线国产日韩 | 姐姐的秘密韩剧免费观看全集中文 | 波多野结衣在线观看视频 | 成人免费网站在线观看 | 深爱激情综合网 | 高h放荡受浪受bl | 好逼天天操 | av午夜激情 | 久久精品人人做人人爽 |