关于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;
}

以我的(不正确)的理解来看,我预想中的代码行为应该是打印出“lr”,即调用了接受const T&参数的process。test(i)接受一个左值实参,根据右值引用的推导规则,T应被推导为int&, 经过std::forward后,根据引用折叠的规则,t为int&,匹配到 void process(const T& t)这个版本。但最后实验结果打印出的是"rv"。在此想请教一下这段代码到底是如何执行的。

阅读 4.1k
1 个回答

既然你了解了引用折叠, 我相信你也应该知道了forward就是一简单的static_cast<T&&>t.

此函数void process(T&& t)是有问题的, 它依旧是一个universal reference/forwarding reference , 只有void process(int&& t)这样明确是右值引用你才能称作rv, 对吧. 所以先改下函数并简化代码:

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只是一个转发(从上面的实现配合引用折叠也是很好理解的), 并且能保留原有的ref-qualifier和const-qualifier, 所以被称作完美转发, 因此你可以把test里面的process继续简化掉:

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&

有没有发现什么? 整个过程其实就是简化成左值, 右值, 加上const-qualifier对process的函数重载决议了.

无论T&&还是const T&都和标题中的forward, 右值引用没什么关系了

这下应该明白了吧? 只有const的左值才会匹配const T&, 其他都会匹配T&&. 很明了的一个重载决议.


继续, 可能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&&, 毕竟人家只能匹配右值嘛, 也是应该的.

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题