【C++11新特性】完美轉(zhuǎn)發(fā)機制

完美轉(zhuǎn)發(fā)

什么是完美轉(zhuǎn)發(fā):它指的是函數(shù)模板可以將自己的參數(shù)“完美”地轉(zhuǎn)發(fā)給內(nèi)部調(diào)用的其它函數(shù)。所謂完美,即不僅能準確地轉(zhuǎn)發(fā)參數(shù)的值,還能保證被轉(zhuǎn)發(fā)參數(shù)的左、右值屬性不變。

舉個例子:

template<typename T>
void function(T t) {
    otherdef(t);
}

function() 函數(shù)模板并沒有實現(xiàn)完美轉(zhuǎn)發(fā)。參數(shù) t 為非引用類型,這意味著在調(diào)用 function() 函數(shù)時,實參將值傳遞給形參的過程就需要額外進行一次拷貝操作;無論調(diào)用 function() 函數(shù)模板時傳遞給參數(shù) t 的是左值還是右值,對于函數(shù)內(nèi)部的參數(shù) t 來說,它有自己的名稱,也可以獲取它的存儲地址,因此它永遠都是左值,也就是說,傳遞給 otherdef() 函數(shù)的參數(shù) t 永遠都是左值。無論從那個角度看,function() 函數(shù)的定義都不“完美”。

完美轉(zhuǎn)發(fā)這樣嚴苛的參數(shù)傳遞機制,C++98標準中幾乎不會用到,但 C++11 標準為 C++ 引入了右值引用和移動語義,因此很多場景中是否實現(xiàn)完美轉(zhuǎn)發(fā),直接決定了該參數(shù)的傳遞過程使用的是拷貝語義(調(diào)用拷貝構(gòu)造函數(shù))還是移動語義(調(diào)用移動構(gòu)造函數(shù))

C++98如何實現(xiàn)完美轉(zhuǎn)發(fā)

事實上,C++98標準下的 C++ 也可以實現(xiàn)完美轉(zhuǎn)發(fā),
使用非const引用作為函數(shù)模板參數(shù)時,只能接收左值,無法接收右值;
使用const 引用既可以接收左值,也可以接收右值.但考慮到其 const 屬性,除非被調(diào)用函數(shù)的參數(shù)也是 const 屬性,否則將無法直接傳遞

用 C++ 98/03 標準下的 C++ 語言,我們可以采用函數(shù)模板重載的方式實現(xiàn)完美轉(zhuǎn)發(fā)
例如:

//接收左值
void otherdef(int & t) {
    cout << "lvalue\n";
}
//接收右值
void otherdef(const int & t) {
    cout << "rvalue\n";
}
//接收右值
template <typename T>
void function(const T& t) {
    otherdef(t);
}
//接收左值
template <typename T>
void function(T& t) {
    otherdef(t);
}
int main()
{
    function(5);//5 是右值
    int  x = 1;
    function(x);//x 是左值
    return 0;
}

程序執(zhí)行結(jié)果為:
rvalue
lvalue

從輸出結(jié)果中可以看到,對于右值 5 來說,它實際調(diào)用的參數(shù)類型為 const T& 的函數(shù)模板,對于左值 x 來說,2 個重載模板函數(shù)都適用,C++編譯器會選擇最適合的參數(shù)類型為 T& 的函數(shù)模板。但是使用次方法是有弊端的,僅適用于模板函數(shù)僅有少量參數(shù)的情況,否則就需要編寫大量的重載函數(shù)模板,造成代碼的冗余。

右值引用

C++11 標準中規(guī)定,通常情況下右值引用形式的參數(shù)只能接收右值,不能接收左值。但對于函數(shù)模板中使用右值引用語法定義的參數(shù)來說,它不再遵守這一規(guī)定,既可以接收右值,也可以接收左值在函數(shù)模板中的右值引用又被稱為萬能引用。

template <typename T>
void function(T&& t) {
    otherdef(t);
}

此模板函數(shù)的參數(shù) t 既可以接收左值,也可以接收右值。

引用折疊

但僅僅使用右值引用作為函數(shù)模板的參數(shù)是遠遠不夠的,還有一個問題繼續(xù)解決,即如果調(diào)用 function() 函數(shù)時為其傳遞一個左值引用或者右值引用的實參,如下所示:

int n = 10;
int & num = n;
function(num); // T 為 int&
int && num2 = 11;
function(num2); // T 為 int &&

C++ 11標準為了更好地實現(xiàn)完美轉(zhuǎn)發(fā),特意為其指定了新的類型匹配規(guī)則,又稱為引用折疊規(guī)則(假設(shè)用 A 表示實際傳遞參數(shù)的類型):
當實參為左值或者左值引用(A&)時,函數(shù)模板中 T&& 將轉(zhuǎn)變?yōu)?A&(A& && = A&);
當實參為右值或者右值引用(A&&)時,函數(shù)模板中 T&& 將轉(zhuǎn)變?yōu)?A&&(A&& && = A&&)。
在實現(xiàn)完美轉(zhuǎn)發(fā)時,只要函數(shù)模板的參數(shù)類型為 T&&,則 C++ 可以自行準確地判定出實際傳入的實參是左值還是右值。

std::forward傳遞屬性

通過將函數(shù)模板的形參類型設(shè)置為 T&&,我們可以很好地解決接收左、右值的問題。但除此之外,還需要解決一個問題,即無論傳入的形參是左值還是右值,對于函數(shù)模板內(nèi)部來說,形參既有名稱又能尋址,因此它都是左值。那么他永遠不會調(diào)用接下來函數(shù)的右值版本.

C++11新標準還引入了一個模板函數(shù) forword<T>(),我們只需要調(diào)用該函數(shù),就可以很方便地將函數(shù)模板接收到的形參連同其左、右值屬性,一起傳遞給被調(diào)用的函數(shù)

//左值版本
void otherdef(int & t) {
    cout << "lvalue\n";
}
//右值版本
void otherdef(const int & t) {
    cout << "rvalue\n";
}
//實現(xiàn)完美轉(zhuǎn)發(fā)的函數(shù)模板
template <typename T>
void function(T&& t) {
    otherdef(forward<T>(t));
}
int main()
{
    function(5);
    int  x = 1;
    function(x);
    return 0;
}
程序執(zhí)行結(jié)果為:
rvalue
lvalue

此 function() 模板函數(shù)才是實現(xiàn)完美轉(zhuǎn)發(fā)的最終版本??梢钥吹剑琭orword() 函數(shù)模板用于修飾被調(diào)用函數(shù)中需要維持參數(shù)左、右值屬性的參數(shù)。

總結(jié)

在定義模板函數(shù)時:

  • 我們采用右值引用的語法格式定義參數(shù)類型,由此該函數(shù)既可以接收外界傳入的左值,也可以接收右值;
  • 還需要使用 C++11 標準庫提供的 forword<T>() 模板函數(shù)修飾被調(diào)用函數(shù)中需要維持左、右值屬性的參數(shù)。由此即可輕松實現(xiàn)函數(shù)模板中參數(shù)的完美轉(zhuǎn)發(fā)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容