首先简单介绍一下几个概念
(1)直接调用:比如从main()主函数中调用funcLast()函数,这其实就叫做直接调用。
(2)转发:从main()函数中调用funcMiddle()函数,通过funcMiddle()函数调用funcLast()函数,这就叫做转发,funcMiddle()函数被当作一个跳板函数。一般情况下跳板函数都写成一个函数模板。
templatevoid func(T& param) { cout << "传入的是左值" << endl; } template void func(T&& param) { cout << "传入的是右值" << endl; } template void funcMiddle(T&& param) { func(param); } int main() { int num = 2021; funcMiddle(num); funcMiddle(2022); system("pause"); return 0; }
结果:
从运行结果可以看出不管传得是左值还是右值最终的调用了左值函数,这不是我们想要的,我们需要传左值则转发最值,传右值则转发右值。那这到底是什么原因呢?
funcMiddle()函数本身的形参是一个万能引用,它即可以接受左值也可以接受右值;第一个funcMiddle()函数调用实参是左值,所以,funcMiddle()函数中调用func()中传入的参数也应该是左值;第二个warp()函数调用实参是右值,根据引用折叠规则,funcMiddle()函数接收的参数类型是右值引用,那么为什么却调用了调用func()的左值版本了呢?这是因为在funcMiddle()函数内部,右值引用类型变为了左值,因为参数有了名称,我们也通过变量名取得变量地址。
那么问题来了,怎么保持函数调用过程中,变量类型的不变呢?这就是我们所谓的“完美转发”技术,在C++11中通过std::forward()函数来实现,这个函数要么返回一个左值,要么返回一个右值,可以说forward是c++ 11标准库提供的专门为转发而存在的函数。我们修改我们的funcMiddle()函数如下:
templatevoid funcMiddle(T&& param) { func(std::forward (param)); }
运行结果:
引用折叠(Universal Collapse)
因为完美转发(Perfect Forwarding)的概念涉及引用折叠。一个模板函数,根据定义的形参和传入的实参的类型,我们可以有下面四中组合:
左值-左值 T& & # 函数定义的形参类型是左值引用,传入的实参是左值引用
左值-右值 T& && # 函数定义的形参类型是左值引用,传入的实参是右值引用
右值-左值 T&& & # 函数定义的形参类型是右值引用,传入的实参是左值引用
右值-右值 T&& && # 函数定义的形参类型是右值引用,传入的实参是右值引用
但是C++中不允许对引用再进行引用,对于上述情况的处理有如下的规则:
所有的折叠引用最终都代表一个引用,要么是左值引用,要么是右值引用。规则是:如果任一引用为左值引用,则结果为左值引用。否则(即两个都是右值引用),结果为右值引用。
即就是前面三种情况代表的都是左值引用,而第四种代表的右值引用。



