在C++11中,&&不再只有邏輯與的含義,還可能是右值引用:
void f(int&& i);
但也不盡然,&&還可能是轉發引用:
templatevoid g(T&& obj);
“轉發引用”(forwarding reference)舊稱“通用引用”(universal reference),它的“通用”之處在於你可以拿一個左值綁定給轉發引用,但不能給右值引用:
void f(int&& i) { } templatevoid g(T&& obj) { } int main() { int n = 2; f(1); // f(n); // error g(1); g(n); }
一個函數的參數要想成為轉發引用,必須滿足:
參數類型為T&&,沒有const或volatile;
T必須是該函數的模板參數。
換言之,以下函數的參數都不是轉發引用:
templatevoid f(const T&&); templatevoid g(typename std::remove_reference&&); templateclass A { templatevoid h(T&&, const U&); };
另一種情況是auto&&變量也可以成為轉發引用:
auto&& vec = foo();
所以寫範圍for循環的最好方法是用auto&&:
std::vectorvec; for (auto&& i : vec) { // ... }
有一個例外,當auto&&右邊是初始化列表,如auto&& l = {1, 2, 3};時,該變量為std::initializer_list
轉發引用,是用來轉發的。只有當你的意圖是轉發參數時,才寫轉發引用T&&,否則最好把const T&和T&&寫成重載(如果需要的話還可以寫T&,還有不常用的const T&&;其中T是具體類型而非模板參數)。
轉發一個轉發引用需要用std::forward,定義在
調用g有幾種可能的參數:
int i = 1; g(i);,T為int&,調用g(int&);
const int j = 2; g(j);,T為const int&,調用g(const int&);
int k = 3; g(std::move(k));或g(4);,T為int(不是int&&哦!),調用g(int&&)。
你也許會疑惑,為什麼std::move不需要
templateconstexpr T&& forward(std::remove_reference_t&) noexcept; templateconstexpr T&& forward(std::remove_reference_t&&) noexcept;
調用std::forward時,編譯器無法根據std::remove_reference_t
但是這並沒有從根本上回答問題,或者可以進一步引出新的問題――為什麼std::forward的參數不定義成T&&呢?
原因很簡單,T&&會把T&、const T&、T&&和const T&&(以及對應的volatile)都吃掉,有了T&&以後,再寫T&也沒用。
且慢,T&&參數在傳入函數是會匹配到T&&嗎?
#include#includevoid 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)); }
不會!程序輸出int&。在函數bar中,i是一個左值,其類型為int的右值引用。更直接一點,它有名字,所以它是左值。
因此,如果std::forward沒有手動指定的模板參數,它將不能區分T&和T&&――那將是“糟糕轉發”,而不是“完美轉發”了。
最後分析一下std::forward的實現,以下代碼來自libstdc++:
templateconstexpr _Tp&& forward(typename std::remove_reference::type& __t) noexcept { return static_cast(__t); } templateconstexpr _Tp&& forward(typename std::remove_reference::type&& __t) noexcept { static_assert(!std::is_lvalue_reference::value, "template argument" " substituting _Tp is an lvalue reference type"); return static_cast(__t); }
當轉發引用T&& obj綁定左值int&時,匹配第一個重載,_Tp即T為int&,返回類型_Tp&&為int&(引用摺疊:& &、& &&、&& &都摺疊為&,只有&& &&摺疊為&&);
const int&同理;
當轉發引用綁定右值int&&時,匹配第二個重載,_Tp為int,返回類型為int&&;
const int&&同理。
綜上,std::forward能完美轉發。
程序員總是要在Stack Overflow上撞撞牆才能學會一點東西。
[qp18502452 ] C++11 模板參數的“右值引用”是轉發引用嗎已經有245次圍觀