詳解C++完美轉發

桂洛克船长發表於2024-07-06

我們先來看摺疊規則

引用摺疊規則

在C++中,引用摺疊規則的主要目的是為了保證在模板推導過程中,對於引數 T&& 能夠正確地推匯出其最終的引用型別,以便進行引數傳遞時的正確行為。下面是引用摺疊規則的總結:

  1. 左值引用摺疊

    • T& & 摺疊為 T&
    • T& && 摺疊為 T&

    這意味著如果一個左值引用被再次引用,或者一個左值引用被右值引用引用,最終的結果仍然是左值引用。就是左值怎麼引用都是左值。

  2. 右值引用摺疊

    • T&& & 摺疊為 T&
    • T&& && 摺疊為 T&&

    這意味著如果一個右值引用被左值引用引用,最終結果是左值引用;如果一個右值引用被右值引用引用,最終結果仍然是右值引用

瞭解了摺疊規則後,我們來看個demo

template <typename F, typename T1, typename T2>
void flip2(F f, T1 &&t1, T2 &&t2)
{

    f(t1, t2);
}

這個函式模板接受三個引數,並將t1和t2作為f的引數進行呼叫,給出f的實現

void gtemp(int&& v1, int& v2)
{
    cout << v1 << " " << v2 << endl;
}

呼叫

int j = 99;
flip2(gtemp, 42, j);
cout << "j is " << j << endl;

得到報錯

error C2664: “void (int &&,int &)”: 無法將引數 1 從“T1”轉換為“int &&”

分析:42是一個右值物件,j是左值,在flip2中形參t1透過摺疊規則推導知型別為右值引用,t2是左值引用,但是將t1作為f的引數時,它依舊是作為一個左值來用的,因此gtemp也就是f呼叫明顯會出錯它的第一個引數型別為右值引用,也就是說flip2函式內部把外部實參42作為其他函式的引數傳遞時,將其的型別給“修改了”,從右值變為左值,這當然不是我們所希望的,那麼怎麼解決呢?

std::forward<>()函式就是用來保持型別不變的。

再看修改demo

template <typename F, typename T1, typename T2>
void flip2(F f, T1 &&t1, T2 &&t2)
{
    f(std::forward<T1>(t1),std::forward<T2>(t2));
}

forward包含在utility庫裡,我們使用完美轉發函式使t1,t2保持型別與外部實參一致,也就是42(右值),j(int)因此程式正常執行。

相關文章