您好,登錄后才能下訂單哦!
小編這次要給大家分享的是C++11模板參數的“右值引用”是不是轉發引用,文章內容豐富,感興趣的小伙伴可以來了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。
在C++11中,&&不再只有邏輯與的含義,還可能是右值引用:
void f(int&& i);
但也不盡然,&&還可能是轉發引用:
template<typename T> void g(T&& obj);
“轉發引用”(forwarding reference)舊稱“通用引用”(universal reference),它的“通用”之處在于你可以拿一個左值綁定給轉發引用,但不能給右值引用:
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); }
一個函數的參數要想成為轉發引用,必須滿足:
換言之,以下函數的參數都不是轉發引用:
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&&變量也可以成為轉發引用:
auto&& vec = foo();
所以寫范圍for循環的最好方法是用auto&&:
std::vector<int> vec; for (auto&& i : vec) { // ... }
有一個例外,當auto&&右邊是初始化列表,如auto&& l = {1, 2, 3};時,該變量為std::initializer_list<int>&&類型。
轉發引用,是用來轉發的。只有當你的意圖是轉發參數時,才寫轉發引用T&&,否則最好把const T&和T&&寫成重載(如果需要的話還可以寫T&,還有不常用的const T&&;其中T是具體類型而非模板參數)。
轉發一個轉發引用需要用std::forward,定義在<utility>中:
調用g有幾種可能的參數:
你也許會疑惑,為什么std::move不需要<T>而std::forward需要呢?這得從std::forward的簽名說起:
template<typename T> constexpr T&& forward(std::remove_reference_t<T>&) noexcept; template<typename T> constexpr T&& forward(std::remove_reference_t<T>&&) noexcept;
調用std::forward時,編譯器無法根據std::remove_reference_t<T>反推出T,從而實例化函數模板,因此<T>需要手動指明。
但是這并沒有從根本上回答問題,或者可以進一步引出新的問題——為什么std::forward的參數不定義成T&&呢?
原因很簡單,T&&會把T&、const T&、T&&和const T&&(以及對應的volatile)都吃掉,有了T&&以后,再寫T&也沒用。
且慢,T&&參數在傳入函數是會匹配到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)); }
不會!程序輸出int&。在函數bar中,i是一個左值,其類型為int的右值引用。更直接一點,它有名字,所以它是左值。
因此,如果std::forward沒有手動指定的模板參數,它將不能區分T&和T&&——那將是“糟糕轉發”,而不是“完美轉發”了。
最后分析一下std::forward的實現,以下代碼來自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); }
綜上,std::forward能完美轉發。
看完這篇關于C++11模板參數的“右值引用”是不是轉發引用的文章,如果覺得文章內容寫得不錯的話,可以把它分享出去給更多人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。