首頁 > 軟體

淺析C++模板型別中的原樣轉發和可變引數的實現

2022-08-09 22:02:21

原樣轉發的意義

前文我們實現了一個my_move函數,用來模擬stl的move操作,實現去參照的功能。其內部的原理就是通過remove_reference實現去參照操作。

有時我們也需要保留原型別的左值或者右值屬性,進行原樣轉發,此時就要用forward實現轉發功能。

我們先定義一個模板函數

template <typename F, typename T1, typename T2>
void flip1(F f, T1 t1, T2 t2)
{
    f(t2, t1);
}

flip1內部呼叫了函數f

我們寫一個函數測試

void ftemp(int v1, int &v2)
{
    cout << v1 << " " << ++v2 << endl;
}
void use_ftemp(){
    int j = 100;
    int i = 99;
    flip1(ftemp, j, 42);
    cout << "i is " << i << " j is " << j << endl;
}

通過列印發現i和j的值沒有變化,因為ftemp的v2引數雖然是參照,但是是flip1的形參t1的參照

t1只是形參,修改t1並不能影響外邊的實參j。

想要達到修改實參的目的,需要將flip1的引數修改為參照,我們先實現修改後的版本flip2

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

我們定義了一個flip2函數,t1和t2分別是右值參照型別。接下來用一個測試函數進行測試

int j = 100;
int i = 99;
flip2(ftemp, j, 42);
cout << "i is " << i << " j is " << j << endl;

這次我們發現j被修改了,因為flip2的t1引數型別為T1的右值參照,當把實參j賦值給flip2時,T1變為int&,

t1的型別就是int& &&,通過摺疊t1變為int&型別。這樣t1就和實參j繫結了,在flip2內部修改t1,就達到了修改j的目的。

但是flip2同樣存在一個問題,如果flip2的第一個引數f,如果f是一個接受右值參照引數的函數,會出現編譯錯誤。

為說明這一點,我們實現一個接納模板引數右值參照型別的函數

void gtemp(int &&i, int &j)
{
    cout << "i is " << i << " j is " << j << endl;
}

此時如果我們將gtemp作為引數傳遞給flip2會報錯

int j = 100;
int i = 99;
// flip2(gtemp, j, 42) 會報錯
// 因為42作為右值純遞給flip2,t2會被摺疊為int&型別
// t2傳遞給gtemp第一個引數時,int&&無法系結int&型別
//flip2(gtemp, i, 42);
cout << "i is " << i << " j is " << j << endl;

當我們將42傳遞給flip2第二個引數時,T2被範例化為int型別,t2就變為int && 型別,通過摺疊t2變為int&型別。

t2作為引數傳遞給gtemp的第一個引數時會報錯,

cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’

因為t2是一個左值,右值無法系結該左值。

解決的辦法就是實現一個flip函數,內部實現對T2,T1型別的原樣轉發。

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

通過forward將t2型別轉化為和T2型別一樣的型別,也就是int的右值型別,接下來的呼叫就不會出問題了

void use_ftemp()
{
    int j = 100;
    int i = 99;
    flip(gtemp, i, 42);
    cout << "i is " << i << " j is " << j << endl;
}

模板的可變引數

模板同樣支援可變引數

//可變引數的函數模板
template <typename T>
ostream &print(ostream &os, const T &t)
{
    return os << t; //輸出最後一個元素
}
template <typename T, typename... Args>
ostream &print(ostream &os, const T &t, const Args &...rest)
{
    os << t << ", ";
    return print(os, rest...);
}

Args是可變的模板引數包, 然後再用Args定義rest變數,這是一個可變參數列。

我們的模板函數print內部呼叫stl的print函數,通過對rest…實現展開操作。

呼叫過程可按如下的方式

void use_printtemp()
{
    int i = 100;
    string s = "hello zack!!!";
    print(cout, i, s, 42);
}

第一次呼叫print實際是呼叫的可變引數的print,之後才呼叫沒有可變引數的print函數。

總結

本文介紹了模板型別的原樣轉發,以及多模板參數列的使用。

視訊連結

原始碼連結

到此這篇關於淺析C++模板型別中的原樣轉發和可變引數有什麼意義的文章就介紹到這了,更多相關C++原樣轉發和可變引數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com