2017年4月7日 星期五

C++ 副程式的 const左右值引用 的完美轉發

C++ 副程式的 const左右值引用 的完美轉發

tags: C++ Concept2

四種不同屬性的函式

已知存在著四種屬性
  • lvalue
  • lvalue const
  • rvalue
  • rvalue const
他們分別呼叫的副程式參數長這個樣子
void fun(int & i){
    cout << "fun L" << endl;
}
void fun(int const & i){
    cout << "fun L const" << endl;
}
void fun(int && i){
    cout << "fun R" << endl;
}
void fun(int const && i){
    cout << "fun R const" << endl;
}
如此宣告我們可以統一使用 fun() 這個名稱呼叫四種不同的函式,分別是這樣呼叫
    int a=0;
    int const b=0;

    fun(a);
    fun(b);
    fun(move(a));
    fun(move(b));

函式轉發

如果你想要用另一個副程式來呼叫這個 fun() 藉此組合更多的功能可能會遇到一些問題
  • const 與 non-const區分
  • 左值與右值區分
  • 右值進來有了名字變成左值

const 與非 const

可以試著重載const藉此可以同時引入兩種版本
template<class T>
void tran(T const & t){
    fun(t);
}
不過如此一來將導致你沒有辦法修改內容,也無從判斷原本是否是const
可以透過&&達到正確的結果
template<class T>
void tran(T && t){
    fun(t);
}
如此由 template 解決引數是否有 const,由 && 解決引數是左值還是右值。

消除名字

不過仍然有一個問題,參數本身是帶有名字的再引入的時候有了名字就變成左值
可以透過 forward<T>(t) 消除名字,獲取原本的屬性,
該是右值就還原右值,該是左值則保持左值
template<class T>
void tran(T && t){
    fun(forward<T>(t));
}
如此一來我們就可以好像換了一個別名似的,用第二個名字自由的操作,可以在函式內加上一些功能,增加代碼的重複性。
template<class T>
void tran(T && t){
    // 其他功能
    fun(forward<T>(t));
}

fun();
tran();
需要 fun() 時使用他,需要fun() 加上一額外的功能使用 tran() ,並可將重複的代寫在一起

為什麼不是使用 move() 語意即可?

確實可以透過move()協助我們呼叫右值引數的函式,但是無從得知原本的屬性是什麼,不管進入函式的是左值還是右值move()一律將其轉為右值;相較於forward()這個能夠還原原本的屬性,原本屬性是左值,出來就是還是左值。
如果要使用 move() 解決那至少也要存在著2個函式才可以解決,一個呼叫左值一個呼叫右值,這是因為無法得知原本屬性,只能給使用者自己判斷。
template<class T>
void tran1(T & t){
    fun(t);
}

template<class T>
void tran2(T && t){
    fun(move(t));
}

參考代碼

/*****************************************************************
Name : 
Date : 2017/04/07
By   : CharlotteHonG
Final: 2017/04/07
*****************************************************************/
#include <iostream>
using namespace std;

void fun(int & i){
    cout << "fun L" << endl;
}
void fun(int const & i){
    cout << "fun L const" << endl;
}
void fun(int && i){
    cout << "fun R" << endl;
}
void fun(int const && i){
    cout << "fun R const" << endl;
}

template<class T>
void tran(T && t){
    fun(forward<T>(t));
}
// template 解決了 const 與 non-constt 的問題,可以還原原本屬性
// && 解決了 LR 屬性問題,可以還原原本左右值屬性
// move提供使用者選擇,可以選擇要叫左值還是右值的函式
// forward 解決了引入的右值有了名字變成左值的問題
/*==============================================================*/
int main(int argc, char const *argv[]){
    int a=0;
    int const b=0;
    // fun(a);
    // fun(b);
    // fun(move(a));
    // fun(move(b));

    tran(a);
    tran(b);
    tran(move(a));
    tran(move(b));
    return 0;
}
/*==============================================================*/

沒有留言:

張貼留言