首先先上代码
#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"。在此想请教一下这段代码到底是如何执行的。
既然你了解了引用折叠, 我相信你也应该知道了forward就是一简单的
static_cast<T&&>t
.此函数
void process(T&& t)
是有问题的, 它依旧是一个universal reference/forwarding reference
, 只有void process(int&& t)
这样明确是右值引用你才能称作rv
, 对吧. 所以先改下函数并简化代码:因为forward只是一个转发(从上面的实现配合引用折叠也是很好理解的), 并且能保留原有的ref-qualifier和const-qualifier, 所以被称作完美转发, 因此你可以把test里面的process继续简化掉:
有没有发现什么? 整个过程其实就是简化成左值, 右值, 加上const-qualifier对process的函数重载决议了.
无论
T&&
还是const T&
都和标题中的forward, 右值引用没什么关系了这下应该明白了吧? 只有const的左值才会匹配
const T&
, 其他都会匹配T&&
. 很明了的一个重载决议.继续, 可能OP会想, 如果我这么重载呢?
都有
const
呀, 此时该怎么办呢? 编译器是不是就gg了?简单的说, 此时
const T&&
不再是人见人爱花见花开的,forwarding reference
, 因为有了const-qualifier, 所以退化成了rvalue-reference
了. g(i)妄想传个左值进去不是作么. 比如这样的例子:有了以上铺垫, OP是不是能想出之前提的问题:
可见只有右值
1
匹配了const T&&
, 毕竟人家只能匹配右值嘛, 也是应该的.