这一节是对上一篇右值引用的补充。
链接: 右值引用
万能引用
看如下代码
void Fun(int &x){ cout << "左值引用" << endl; } void Fun(const int &x){ cout << "const 左值引用" << endl; } void Fun(int &&x){ cout << "右值引用" << endl; }
fun()函数进行了重构,当我们调用函数时,编译器会根据我们的实参来判断调用最匹配的函数,这是的大家都知道的事情。
但是,
模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
当传右值时,正常调用
当传递左值时,&& 会进行折叠,所以也叫折叠引用
看接下来的代码:
template<class T> void func1(T&& z) { fun(z); } int main() { int a=10; func1(10); //右值 func1(a); //右值 return 0; }
同时传左值和右值,也可以正常运行,当然
void func1(const int& z){}
也可以成功调用上述代码,但是该方法无法保存实参的右值属性,在传递过程中就改变了参数的属性。
运行结果:
当调用了fun()函数后,怎么显示的都是左值引用怎么回事呢?
要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是ref引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用,是不是感觉很神奇。
10 //纯右值 int&& ref=10; // ref是一个左值
所以当右值传递给z后,z已经是一个左值了,所以最后的结果是打印出两次的左值引用。
那么如何解决呢?
就需要学习接下来的完美转发。
完美转发
std::forward 完美转发在传参的过程中保留对象原生类型属性。
template<class T> void func1(T&& z) { fun(forward<T>(z)); } int main() { int a=10; func1(10); //右值 func1(a); //右值 return 0; }
再次运行:
可以看出,一个右值引用一个左值引用,保持对象原生属性类型。
这对右值引用做了很好的一个补充,我们在实习一些比较复杂的项目中,在某一个函数复用函数是很常见的行为,经常叠加了好几层复用,如果传递右值,就需要完美转发来保存其原生属性。
注意:对每一层的调用都需要进行一次完美转发:forward(),否则参数的原生属性还是会改变。