C++11 模板參數(shù)的“右值引用”是轉(zhuǎn)發(fā)引用嗎
在C++11中,&&不再只有邏輯與的含義,還可能是右值引用:
void f(int&& i);
但也不盡然,&&還可能是轉(zhuǎn)發(fā)引用:
template<typename T> void g(T&& obj);
“轉(zhuǎn)發(fā)引用”(forwarding reference)舊稱“通用引用”(universal reference),它的“通用”之處在于你可以拿一個(gè)左值綁定給轉(zhuǎn)發(fā)引用,但不能給右值引用:
void f(int&& i) { } template<typename T> void g(T&& obj) { } int main() { int n = 2; f(1); // f(n); // error g(1); g(n); }
一個(gè)函數(shù)的參數(shù)要想成為轉(zhuǎn)發(fā)引用,必須滿足:
- 參數(shù)類型為T&&,沒有const或volatile;
- T必須是該函數(shù)的模板參數(shù)。
換言之,以下函數(shù)的參數(shù)都不是轉(zhuǎn)發(fā)引用:
template<typename T> void f(const T&&); template<typename T> void g(typename std::remove_reference<T>&&); template<typename T> class A { template<typename U> void h(T&&, const U&); };
另一種情況是auto&&變量也可以成為轉(zhuǎn)發(fā)引用:
auto&& vec = foo();
所以寫范圍for循環(huán)的最好方法是用auto&&:
std::vector<int> vec; for (auto&& i : vec) { // ... }
有一個(gè)例外,當(dāng)auto&&右邊是初始化列表,如auto&& l = {1, 2, 3};時(shí),該變量為std::initializer_list<int>&&類型。
轉(zhuǎn)發(fā)引用,是用來(lái)轉(zhuǎn)發(fā)的。只有當(dāng)你的意圖是轉(zhuǎn)發(fā)參數(shù)時(shí),才寫轉(zhuǎn)發(fā)引用T&&,否則最好把const T&和T&&寫成重載(如果需要的話還可以寫T&,還有不常用的const T&&;其中T是具體類型而非模板參數(shù))。
轉(zhuǎn)發(fā)一個(gè)轉(zhuǎn)發(fā)引用需要用std::forward,定義在<utility>中:
調(diào)用g有幾種可能的參數(shù):
- int i = 1; g(i);,T為int&,調(diào)用g(int&);
- const int j = 2; g(j);,T為const int&,調(diào)用g(const int&);
- int k = 3; g(std::move(k));或g(4);,T為int(不是int&&哦?。?,調(diào)用g(int&&)。
你也許會(huì)疑惑,為什么std::move不需要<T>而std::forward需要呢?這得從std::forward的簽名說(shuō)起:
template<typename T> constexpr T&& forward(std::remove_reference_t<T>&) noexcept; template<typename T> constexpr T&& forward(std::remove_reference_t<T>&&) noexcept;
調(diào)用std::forward時(shí),編譯器無(wú)法根據(jù)std::remove_reference_t<T>反推出T,從而實(shí)例化函數(shù)模板,因此<T>需要手動(dòng)指明。
但是這并沒有從根本上回答問(wèn)題,或者可以進(jìn)一步引出新的問(wèn)題——為什么std::forward的參數(shù)不定義成T&&呢?
原因很簡(jiǎn)單,T&&會(huì)把T&、const T&、T&&和const T&&(以及對(duì)應(yīng)的volatile)都吃掉,有了T&&以后,再寫T&也沒用。
且慢,T&&參數(shù)在傳入函數(shù)是會(huì)匹配到T&&嗎?
#include <iostream> #include <utility> void foo(int&) { std::cout << "int&" << std::endl; } void foo(const int&) { std::cout << "const int&" << std::endl; } void foo(int&&) { std::cout << "int&&" << std::endl; } void bar(int&& i) { foo(i); } int main() { int i; bar(std::move(i)); }
不會(huì)!程序輸出int&。在函數(shù)bar中,i是一個(gè)左值,其類型為int的右值引用。更直接一點(diǎn),它有名字,所以它是左值。
因此,如果std::forward沒有手動(dòng)指定的模板參數(shù),它將不能區(qū)分T&和T&&——那將是“糟糕轉(zhuǎn)發(fā)”,而不是“完美轉(zhuǎn)發(fā)”了。
最后分析一下std::forward的實(shí)現(xiàn),以下代碼來(lái)自libstdc++:
template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept { return static_cast<_Tp&&>(__t); } template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcept { static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument" " substituting _Tp is an lvalue reference type"); return static_cast<_Tp&&>(__t); }
- 當(dāng)轉(zhuǎn)發(fā)引用T&& obj綁定左值int&時(shí),匹配第一個(gè)重載,_Tp即T為int&,返回類型_Tp&&為int&(引用折疊:& &、& &&、&& &都折疊為&,只有&& &&折疊為&&);
- const int&同理;
- 當(dāng)轉(zhuǎn)發(fā)引用綁定右值int&&時(shí),匹配第二個(gè)重載,_Tp為int,返回類型為int&&;
- const int&&同理。
綜上,std::forward能完美轉(zhuǎn)發(fā)。
程序員總是要在Stack Overflow上撞撞墻才能學(xué)會(huì)一點(diǎn)東西。
到此這篇關(guān)于C++11 模板參數(shù)的“右值引用”是轉(zhuǎn)發(fā)引用嗎的文章就介紹到這了,更多相關(guān)C++11 右值引用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言初識(shí)動(dòng)態(tài)內(nèi)存管理malloc calloc realloc free函數(shù)
動(dòng)態(tài)內(nèi)存是相對(duì)靜態(tài)內(nèi)存而言的。所謂動(dòng)態(tài)和靜態(tài)就是指內(nèi)存的分配方式。動(dòng)態(tài)內(nèi)存是指在堆上分配的內(nèi)存,而靜態(tài)內(nèi)存是指在棧上分配的內(nèi)存2022-03-03C語(yǔ)言每日練習(xí)之進(jìn)制轉(zhuǎn)換
這篇文章主要介紹了C語(yǔ)言進(jìn)制轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-11-11C++實(shí)現(xiàn)讀入二進(jìn)制數(shù)并轉(zhuǎn)換為十進(jìn)制輸出
本文給大家介紹的是一則使用C++實(shí)現(xiàn)讀入二進(jìn)制數(shù)并轉(zhuǎn)換為十進(jìn)制輸出的代碼,實(shí)現(xiàn)起來(lái)其實(shí)非常簡(jiǎn)單,C++本身就提供了二進(jìn)制類庫(kù)的,大家看代碼吧,簡(jiǎn)單又實(shí)用。2015-03-03C++實(shí)現(xiàn)圖片轉(zhuǎn)base64的示例代碼
Base64就是一種 基于64個(gè)可打印字符來(lái)表示二進(jìn)制數(shù)據(jù)的表示方法,本文主要為大家詳細(xì)介紹了如何使用C++實(shí)現(xiàn)圖片轉(zhuǎn)base64,需要的可以參考下2024-04-04C語(yǔ)言 棧的表示和實(shí)現(xiàn)詳細(xì)介紹
這篇文章主要介紹了C語(yǔ)言 棧的表示和實(shí)現(xiàn)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12VS Code C/C++環(huán)境配置教程(無(wú)法打開源文件“xxxxxx.h”或者檢測(cè)到 #include 錯(cuò)誤,請(qǐng)更新in
這篇文章主要介紹了VS Code C/C++環(huán)境配置教程(無(wú)法打開源文件“xxxxxx.h” 或者 檢測(cè)到 #include 錯(cuò)誤。請(qǐng)更新includePath) (POSIX API),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08