item 24: 區分右值引用和universal引用

本文翻譯自《effective modern C++》,由于水平有限,故無法保證翻譯完全正確,歡迎指出錯誤。謝謝!

博客已經遷移到這里啦

古人曾說事情的真相會讓你覺得很自在,但是在適當的情況下,一個良好的謊言同樣能解放你。這個Item就是這樣一個謊言。但是,因為我們在和軟件打交道,所以讓我們避開“謊言”這個詞,換句話來說:本Item是由“抽象”組成的。

為了聲明一個指向T類型的右值引用,你會寫T&&。因此我們可以“合理”地假設:如果你在源代碼中看到“T&&”,你就看到了一個右值引用。可惜地是,它沒有這么簡單:

void f(Widget&& param);         // 右值引用Widget&& var1 = Widget();       // 右值引用auto&& var2 = var1;             // 不是右值引用template<typename T>
void f(std::vector<T>&& param); // 右值引用template<typename T>
void f(T&& param);              // 不是右值引用

事實上,“T&&”有兩個不同的意思。當然,其中一個是右值引用。這樣引用行為就是你所期望的:它們只綁定到右值上去,并且它們的主要職責就是去明確一個對象是可以被move的。

“T&&”的另外一個意思不是左值引用也不是右值引用。這樣的引用看起來像是在源文件中的右值引用(也就是,“T&&”),但是它能表現得像是一個左值引用(也就是“T&”)一樣。它這樣的兩重意義讓它能綁定到左值(就像左值引用)上去,也能綁定到右值(就像右值引用)上去。另外,它能綁定到const或非const對象上去,也能綁定到volatile或非volatile對象上去,甚至能綁定到const加volatile的對象上去。它能綁定到幾乎任何東西上去。這樣空前靈活的引用理應擁有它們自己的名字,我叫它們universal引用(萬能引用)。

universal引用出現在兩種上下文中。最通用的情況是在函數模板參數中,就像來自于上面示例代碼的這個例子一樣:

template<typename T>
void f(T&& param);              // param是一個universal引用

第二個情況是auto聲明,包括上面示例代碼中的這一行代碼:

auto&& var2 = var1;             // var2是一個universal引用

這兩個情況的共同點就是它們都存在類型推導。在模板f中,param的類型正在被推導,并且在var2的聲明式中,var2的類型正在被推導。把它們和下面的例子(它們不存在類型推導,同樣來自上面的示例代碼)比較一下,可以發現,如果你看到不存在類型推導的“T&&”時,你能把它視為右值引用:

void f(Widget&& param);         // 沒有類型推導// param是右值引用Widget&& var1 = Widget();       // 沒有類型推導// param是右值引用

因為universal引用是引用,它們必須被初始化。universal引用的初始化決定了它代表一個右值還是一個左值。如果初始化為一個右值,universal引用對應右值引用。如果初始化為一個左值,universal引用對應一個左值引用。對于那些屬于函數參數的universal引用,它在調用的地方被初始化:

template<typename T>
void f(T&& param);              // param是一個universal引用Widget w;
f(w);                           // 左值被傳給f,param的類型是// Widget&(也就是一個左值引用)f(std::move(w));                // 右值被傳給f,param的類型是// Widget&&(也就是一個右值引用)

要讓一個引用成為universal引用,類型推導是其必要不補充條件。引用聲明的格式必須同時正確才行,而且格式很嚴格。它必須正好是“T&&”。再看一次這個我們之前在示例代碼中看過的例子:

template<typename T>
void f(std::vector<T>&& param); // param是一個右值引用

當f被調用時,類型T將被推導(除非調用者顯式地指定它,這種邊緣情況我們不關心)。但是param類型推導的格式不是“T&&”,而是“std::vector&&”。按照上面的規則,排除了param成為一個universal引用的可能性。因此param是一個右值引用,有時候你的編譯器會很高興地為你確認你是否傳入了一個左值給f:

std::vector<int> v;
f(v);                           // 錯誤!不能綁定一個左值到右值// 引用上去

甚至一個簡單的const屬性的出場就足以取消引用成為universal的資格:

template<typename T>
void f(const T&& param);        // param是一個右值引用

如果你在一個模板中,并且你看到一個“T&&”類型的函數參數,你可能覺得你能假設它是一個universal引用。但是你不能,因為在模板中不能保證類型推導的存在。考慮一下std::vector中的這個push_back成員函數:

template<class T, class Allocator = allocator<T>>       //來自c++標準庫
class vector {
public:void push_back(T&& x);...
};

push_back的參數完全符合universal引用的格式,但是在這個情況中沒有類型推導發生。因為push_back不能存在于vector的特定實例之外,并且實例的類型就完全能決定push_back的聲明類型了。也就是說

    std::vector<Widget> v;

使得std::vector模板被實例化為下面這樣:

class vector<Widget, allocator<Widget>> {
public:void push_back(Widget&& x);     //右值引用...
};

現在你能清楚地發現push_back沒有用到類型推導。vector的這個push_back(vector中有兩個push_back函數)總是聲明一個類型是rvalue-reference-to-T(指向T的右值引用)的參數。

不同的是,std::vector中和push_back概念上相似的emplace_back成員函數用到了類型推導:

template<class T, class Allocator = allocator<T>>
class vector {
public:template <class... Args>void emplace_back(Args&&... args);...
};

在這里,類型參數Args獨立于vector的類型參數T,所以每次emplace_back被調用的時候,Args必須被推導。(好吧,Args事實上是一個參數包,不是一個類型參數,但是為了討論的目的,我們能把它視為一個類型參數。)

事實上emplace_back的類型參數被命名為Args(不是T),但是它仍然是一個universal引用,之前我說universal引用的格式必須是“T&&”。在這里重申一下,我沒要求你必須使用名字T。舉個例子。下面的模板使用一個universal引用,因為格式(“type&&”)是正確的,并且param的類型將被推導(再說一次,除了調用者顯式指定類型的邊緣情況):

    template<typename MyTemplateType>       // param是一個void someFunc(MyTemplateType&& param);  // universal引用

我之前說過auto變量也能是universal引用。更加精確一些,用auto&&的格式被推導的變量是universal引用,因為類型推導有發生,并且它有正確的格式(“T&&”)。auto universal引用不像用于函數模板參數的universal引用那么常見,但是他們有時候會在C++11中突然出現。他們在C++14中出現的頻率更高,因為C++14的lambda表達式可以聲明auto&&參數。舉個例子,如果你想要寫一個C++14的lambda來記錄任意函數調用花費的時間,你能這么做:

auto timeFuncInvocation =[](auto&& func, auto&&... param){start timer;std::forward<decltype(func)>(func){             // 用paramsstd::forward<decltype(params)>(params)...   // 調用func};停止timer并記錄逝去的時間。};

如果你對lambda中“std::forward<decltype(blah blah blah)>”(譯注:blah blah blah發音為:布拉布拉布拉)的代碼感到困惑,這可能只是意味著你還沒讀過Item 33.不要擔心這件事。在本Item中,重要的事情是lambda表達式中聲明的auto&&參數。func是一個universal引用,它能被綁定到任何調用的對象上去,不管是左值還是右值。params(譯注:原文為args,應該是筆誤)是0個或多個universal引用(也就是一個universal引用包),它能被綁定到任何數量的任意類型的對象上去。結果就是,由于auto universal引用的存在,timeFuncInvocation能給絕大多數函數的執行進行計時。(對于為什么是絕大多數而不是任意,請看Item 30。)

把這件事記在心里:我們這整個Item(universal引用的基礎)都是一個謊言...額,一個“抽象”!潛在的事實被稱為引用折疊,這個話題會在Item 28中專門討論。但是事實并不會讓抽象失效。區分右值引用和universal引用將幫助你更精確地閱讀源代碼(“我看到的T&&只能綁定到右值上,還是能綁定到所有東西上呢?”),并且在你和同事討論的時候,它能讓你避免歧義。(“我在這里使用一個universal引用,不是一個右值引用...”)。它也能讓你搞懂Item 25和Item 26的意思,這兩個Item都依賴于這兩個引用的區別。所以,擁抱抽象并陶醉于此吧。就像牛頓的運動定律(學術上來說是錯誤的)一樣,比起愛因斯坦的相對論(“事實”)來說它通常一樣好用并且更簡單,universal引用的概念也是這樣,比起工作在引用折疊的細節來說,它是更好的選擇。

            你要記住的事
  • 如果一個函數模板參數有T&&的格式,并且會被推導,或者一個對象使用auto&&來聲明,那么參數或對象就是一個universal引用。
  • 如果類型推導的格式不是準確的type&&,或者如果類型推導沒有發生,type&&就是一個右值引用。
  • 如果用右值來初始化,universal引用相當于右值引用。如果用左值來初始化,則相當于左值引用。

轉載于:https://www.cnblogs.com/boydfd/p/5251797.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/257586.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/257586.shtml
英文地址,請注明出處:http://en.pswp.cn/news/257586.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

WebLogic11g-常用運維操作

轉自&#xff1a;https://dead-knight.iteye.com/blog/1940399 希望這篇能把weblogic運維時經常遇到的問題、常用的配置匯總到一起。 1、配置jvm參數&#xff1a; 一般在domain啟動過程中會看到以下啟動的日志信息&#xff0c;如下圖所示&#xff1a; 圖中紅色方框部分為啟動we…

牛腩新聞發布系統(一):SQLHelper重構(一)

導讀&#xff1a;在機房重構的時候&#xff0c;就用到了SQLHelper&#xff0c;但那時候即使把代碼反復看了很多遍&#xff0c;也看了注釋&#xff0c;還和同學交流&#xff0c;也依然是半懂不懂。現在&#xff0c;我再次用到了SQLhelper這個東西&#xff0c;就來說說SQLHelper是…

OPENCV圖像輪廓檢測

前面在圖像轉換的時候學到canny算子,可以檢測出圖像的輪廓信息,但是,該算子檢測到的輪廓信息還需要我們手動的用眼睛去識別,而實際工程應用中,我們需要得到輪廓的具體數學信息,這就涉及到今天的主題,圖像輪廓檢測. 一.圖像輪廓檢測 在opencv中,輪廓對應著一系列的點的集合,open…

mysql 5.7.11 授權_mysql 5.7.11 安裝配置教程

六步輕松搞定mysql5.7.11的安裝1、下載安裝包。mysql-5.7.11版本&#xff1a;2、拷貝到任意盤&#xff1a;例如&#xff0c;解壓后拷貝文件夾至C盤&#xff1a;C:\Program Files\mysql。建議文件夾名字使用英文。3、配置環境變量&#xff1a;計算機—>右鍵—>高級系統設置…

iOS 面試之Block

轉自&#xff1a;http://blog.csdn.net/xunyn/article/details/11658261 1 什么是block 對于閉包&#xff08;block),有很多定義&#xff0c;其中閉包就是能夠讀取其它函數內部變量的函數&#xff0c;這個定義即接近本質又較好理解。對于剛接觸Block的同學&#xff0c;會覺得有…

當安全遇到大數據 “永恒之藍”也將無所遁形!

文章講的是當安全遇到大數據 “永恒之藍”也將無所遁形&#xff01;5月12日&#xff0c;席卷全球的勒索病毒“永恒之藍”讓全世界都為之震動&#xff0c;這是迄今為止全球最大規模的勒索病毒網絡攻擊&#xff0c;100多個國家受到病毒感染&#xff0c;國內中石油、公安內網、高校…

[ES] 安裝

1.ElasticSearch安裝的準備工作 Linux&#xff1a;CentOS6.4 Elasticsearc:elasticsearch-2.2.0 JDK:jdk-7u79-linux-x64 IK:1.8.0 MAVEN:apache-maven-3.3.3-bin 2.配置網絡靜態文件 虛擬機設置橋接模式 配置&#xff1a;vim /etc/sysconfig/network-scripts/ifcfg-eth0 DEVIC…

語言基礎之description方法

1.description方法的一般用處 1: // 指針變量的地址 2: NSLog("%p", &p); 3: // 對象的地址 4: NSLog("%p", p); 5: // <類名&#xff1a;對象地址> 6: NSLog("%", p); 1: Class c [Person class]; 2: …

亞信安全協助綠谷制藥確保“秘方”安全

近幾年&#xff0c;我國醫藥生物技術發展態勢迅猛&#xff0c;加強知識產權保護己成為當務之急。為確保制藥配方數據和生產管理信息系統安全&#xff0c;上海綠谷制藥有限公司采用亞信安全服務器深度安全防護系統&#xff08;Deep Security&#xff09;和亞信安全防毒墻網絡版&…

mysql判斷疊字_格律詩的八大語法特點

古風的語法&#xff0c;本來就和散文的語法大致相同&#xff0c;直到近體詩&#xff0c;才漸和散文不同&#xff0c;原因是&#xff0c;首先在區區五字或七字之中&#xff0c;要施展豐富的想象&#xff0c;不能不力求簡潔&#xff0c;凡可省去而不至于影響語意的字&#xff0c;…

旅游行業春節檔期的大數據營銷

本文講的是旅游行業春節檔期的大數據營銷,雖然我國是以傳統農耕文化為主導的社會&#xff0c;每逢春節講究返鄉團聚。但現代化的城市文明更是對很多人的生活方式產生了影響&#xff0c;特別是生活在大城市中的年輕人&#xff0c;以及由年輕人構成的小家庭來說&#xff0c;春節的…

openwrt lamp

https://applefreak111.wordpress.com/2013/03/12/howtoopenwrt-lamp-stack%E5%AE%89%E8%A3%9D/opkg update安裝Lighttpd, MySQL 5, 和PHP 5。opkg install lighttpd lighttpd-mod-cgi lighttpd-mod-fastcgivi /etc/lighttpd/lighttpd.confcgi.assign ( “.php” > “/usr/…

MySQL本天早上8點到明早8點_似乎找到 OSChina 早上 8 點鐘容易宕機的原因

最近一段時間&#xff0c;OSChina 網站在早上 8 點出頭的時候很容易因為數據庫連接池爆滿而導致網站宕機。表現的情況是數據庫處理大量的查詢&#xff0c;堆積大量并發連接&#xff0c;導致無法再連接到數據庫&#xff0c;執行一個簡單的查詢速度也非常慢&#xff0c;數據庫機器…

基于Eclipse搭建STM32開源開發環境

最近項目不忙&#xff0c;想著沒事看看簡單的嵌入式&#xff0c;弄弄物聯網什么的。于是就從廉價的STM32開刀了。因為一直是做PC軟件開發的&#xff0c;那VS的智能感知那叫一個爽啊&#xff0c;相比之下&#xff0c;覺得這個Keil簡直就像文本編輯器一樣low。于是想換一個開發環…

數據中心不再有空調、風扇等冷卻裝置會怎樣?

數據中心的變革有望依賴移動設備實現&#xff0c;手機里輕便設備或將成為下一代數據中心的基礎設施&#xff0c;服務Google和Facebook等大型的應用程序服務企業。同時&#xff0c;這種商業模式也會構建新一代企業的發展形態&#xff0c;為初創企業帶來前所未有的機遇。 CSDN大數…

.NET 數據庫緩存依賴策略實現

處理大型門戶網站 一般都需要 使用緩存技術這個web加速器在 PHP 和 java 一般 使用的是 基于squid 來做. 當然在 windows .NET 平臺也是可以的 squid有 windows版本.這個以后再去研究,現在 就介紹一下 .NET 自帶的 緩存策略.Microsoft的petshop就用到了它;  一、基于數據庫觸…

大數據面臨的挑戰:當大數據遭遇云計算

本文講的是大數據面臨的挑戰&#xff1a;當大數據遭遇云計算,大數據正在徹底改變IT世界。那么&#xff0c;什么樣的數據談得上數據呢? 根據IDC的報告&#xff0c;未來十年全球大數據將增加50倍。僅在2011年&#xff0c;我們就將看到1.8ZB(也就是1.8萬億GB)的大數據創建產生。這…

Climbing Stairs - Print Path

stair climbing&#xff0c; print out all of possible solutions of the methods to climb a stars, you are allowed climb one or two steps for each time; what is time/space complexity? &#xff08;use recursion&#xff09; 這道題難是難在這個ArrayList<Strin…

java 單例設計_Java 之單例設計模式

設計模式: 對問題行之有效的解決方式, 其實它是一種思想.單例設計模式解決的問題:就是可以保證一個類在內存中的對象唯一性. 即單個實例.比如對于A 和 B 兩個程序使用同一個配置信息對象時, A 對配置信息作出修改, B 也與之對應的更新配置信息, 即需要保證該對象的唯一性.如何保…

Javascript之RegExp

RegExp對象的構造器 new RegExp(pattern[, flags]) pattern 正則表達式文本flags 該參數可以是下面幾個值的任意組合&#xff1a;g 全局匹配i 忽略大小寫m 讓開始和結束字符&#xff08;^ 和 $&#xff09;工作在多行模式&#xff08;也就是&#xff0c;^ 和 $ 可以匹配字符串中…