目錄
- 一. 前言
- 二. 引用折疊
- 引用折疊的規則
- 三. 完美轉發
- 完美轉發適用場景
- 完美轉發底層實現
- 思考1
- 思考2
一. 前言
在函數傳參時,如果想保持某個參數的屬性不改變,需要完美轉發,而完美轉發的實現需要折疊引用的幫助
二. 引用折疊
在語法上,c++不能定義引用的引用,如 int & && r=i,這樣會報錯,但是通過模板或typedef的類型可以構成引用的引用,
引用折疊的規則
- 除了右值引用的右值引用,其他組和都是左值
-
T& & → T& (左值引用的左值引用折疊為左值引用)
-
T& && → T& (左值引用的右值引用折疊為左值引用)
-
T&& & → T& (右值引用的左值引用折疊為左值引用)
-
T&& && → T&& (右值引用的右值引用折疊為右值引用)
- 通過引用規則我們發現,T&&的結果可以保持T本身的屬性,所以T&&也被稱為萬能引用,
三. 完美轉發
完美轉發適用場景
從下圖可以看出,在最開始Function函數中傳入的是右值,但在Func中卻變成了左值,這是由于右值表達式的屬性是左值,
這樣就破壞了最開始傳入的值的屬性
想要保持表達式的屬性,就需要用完美轉發來解決,見下圖
完美轉發底層實現
完美轉發的本質是函數模板,通過引用折疊實現,在forward內部進行強轉,然后再引用折疊返回,
std::remove_reference_t& t 移除T最外層的引用修飾,使不管傳入什么類型,t的類型都是左值引用類型
思考1
思考1:為什么需要保證t的類型是左值引用?
直接用原類型不行么 T&& forward(T&& t)
傳入左值時,T被推導為T&,T&->T&,沒有問題
傳入右值時,T被推導為T,T->T&&,左值向右值轉化,危險操作
template <class T>
T&& forward(std::remove_reference_t<T>& t)
{//std::remove_reference_t<T> 類型萃取模板,移除T的最外層引用修飾,return static_cast<T&&>(t);·
}
template <class T>
void Function(T&& t)
{//Func(std::forward<T>(t));Func(forward<T>(t));//Func(t);
}int main()
{int&& n = 10;Function(n);return 0;
}
思考2
思考2:為什么需要用std::remove_reference_t& 來移除最外層修飾的引用,
T&& forward(T& t) 不行么?
傳入左值時
T& && forward(T& & ) 實例化-> T& forward(T & ) 沒有問題
傳入右值時
T&& && forward(T&& & ) 實例化-> T&& forward(T & ) 看似沒有問題
雖然 int&& & 理論上可以折疊為 int&,但 C++ 類型系統禁止直接聲明這種組合: