鍍金池/ 問答/C++/ 關(guān)于std::forward、右值引用的一些問題

關(guān)于std::forward、右值引用的一些問題

首先先上代碼

#include <utility>
#include <iostream>

using std::cout;
using std::endl;

template
<typename T>
void process(const T& t)
{
    cout << "lr" << endl;
}

template
<typename T>
void process(T&& t)
{
    cout << "rv" << endl;
}

template
<typename T>
void test(T&& t)
{
    process(std::forward<T>(t));
}

int main()
{
    int i = 1;
    test(i);
    getchar();
    return 0;
}

以我的(不正確)的理解來看,我預(yù)想中的代碼行為應(yīng)該是打印出“l(fā)r”,即調(diào)用了接受const T&參數(shù)的process。test(i)接受一個左值實參,根據(jù)右值引用的推導(dǎo)規(guī)則,T應(yīng)被推導(dǎo)為int&, 經(jīng)過std::forward后,根據(jù)引用折疊的規(guī)則,t為int&,匹配到 void process(const T& t)這個版本。但最后實驗結(jié)果打印出的是"rv"。在此想請教一下這段代碼到底是如何執(zhí)行的。

回答
編輯回答
爆扎

既然你了解了引用折疊, 我相信你也應(yīng)該知道了forward就是一簡單的static_cast<T&&>t.

此函數(shù)void process(T&& t)是有問題的, 它依舊是一個universal reference/forwarding reference , 只有void process(int&& t)這樣明確是右值引用你才能稱作rv, 對吧. 所以先改下函數(shù)并簡化代碼:

template <typename T> void process(const T& t) { cout << "const T&" << endl; }
template <typename T> void process(T&& t)      { cout << "T&&" << endl; }
void test(...) { process(...) ;}

因為forward只是一個轉(zhuǎn)發(fā)(從上面的實現(xiàn)配合引用折疊也是很好理解的), 并且能保留原有的ref-qualifier和const-qualifier, 所以被稱作完美轉(zhuǎn)發(fā), 因此你可以把test里面的process繼續(xù)簡化掉:

int non_const_a = 1;
int const const_a = 1; 
template <typename T> void process(const T& t) { cout << "const T&" << endl; }
template <typename T> void process(T&& t)      { cout << "T&&" << endl; }
test(1); // T&&
test(non_const_a); // T&&
test(const_a); // const T&

有沒有發(fā)現(xiàn)什么? 整個過程其實就是簡化成左值, 右值, 加上const-qualifier對process的函數(shù)重載決議了.

無論T&&還是const T&都和標(biāo)題中的forward, 右值引用沒什么關(guān)系了

這下應(yīng)該明白了吧? 只有const的左值才會匹配const T&, 其他都會匹配T&&. 很明了的一個重載決議.


繼續(xù), 可能OP會想, 如果我這么重載呢?

template <typename T> void process(const T& t) { cout << "const T&" << endl; }
template <typename T> void process(const T&& t)      { cout << "T&&" << endl; }

都有const呀, 此時該怎么辦呢? 編譯器是不是就gg了?

clipboard.png

簡單的說, 此時const T&&不再是人見人愛花見花開的, forwarding reference, 因為有了const-qualifier, 所以退化成了rvalue-reference了. g(i)妄想傳個左值進去不是作么. 比如這樣的例子:

void f(int&& a) {} 
int main()
{
    int a = 1;
    f(a);
}

prog.cc:5:12: error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'

 f(a);

有了以上鋪墊, OP是不是能想出之前提的問題:

#include <utility>
#include <iostream>

using std::cout;
using std::endl;

template
<typename T>
void process(const T& t)
{
    cout << "const T&" << endl;
}

template
<typename T>
void process(const T&& t)
{
    cout << "const T&&" << endl;
}

template
<typename T>
void test(T&& t)
{
    process(std::forward<T>(t));
}

int main()
{
    int a = 1;
    const int const_a = 1;
    test(1);
    test(a);
    test(const_a);
}
const T&&
const T&
const T&

可見只有右值1匹配了const T&&, 畢竟人家只能匹配右值嘛, 也是應(yīng)該的.

2017年12月17日 22:16