左值引用、右值引用、万能引用
左值引用和右值引用
例1
#include <iostream>using namespace std;void f1(int& a){cout << "f1: " << a <<endl;
}int main(){int a =8;f1( a ); // complie f1( 5 ); // can't compliereturn 0;
}
非常简单的代码, 函数 void f1(int& )
需要的一个参数是一个引用, 但是 f1(a)
编译器可以通过, f1(5)
编译器不能通过,因为后者就是一个右值(具体左值和右值是什么可以在网上找,其实很容易理解,我们现在的重点是为什么会出现右值引用这个东西)。 对于一个常数,没有分配内存空间,自然就不能有地址,那么引用是需要对方的地址的,那么就不通过。
其实这是因为引用太方便了,相比于指针,我们可以看这个:
例2:
#include <iostream>using namespace std;void f1(int* a){cout << "f1: " << *a <<endl;
}int main(){int a =8;f1( &a ); // can complie f1( (int*)5 ); // can compliereturn 0;
}
这个例子里面的函数void f1(int* a)
就是使用指针作为参数传递,因为指针接收的是地址,那么如果接收的是一个右值5, 可以通过强制类型转换把 这个5当成一个地址,那么函数实际上根据这个地址找对应的变量, 因此可以通过编译,或者骗过编译器! 不过这么做很危险,只是举个例子,因为 5 这个其实并不是某个合法的变量地址,所有会导致非法内存访问,程序崩溃。
通过 例1 、例2 就可以知道了,因为引用相当于是直接传递某一个变量而不是地址, 当然有时候也希望传递一个常量,假设这个函数就是利用这个值而不是改变它的话。
那么 如何能引用右值呢?
方法1: 采用 “const + 引用”的方式,即 void f1(const int& a)
, 不过这意味着在这个函数的生命期内无法改变该引用的值。
方法2:采用右值引用 void f1(int&& a)
,这样就不可以左值引用。
方法3:采用 “函数模版+万用引用”,什么意思呢? 就是C++里面有 万用引用,就是既可以支持左值也可以支持右值,不过这个原理是通过C++ 特有的特性函数模板来推导: 如例3 所示:
例3
#include <iostream>using namespace std;template<typename T>
void f1( T&& a){cout << "f1: " << a <<endl;
}int main(){int a =8;f1( a ); // can complie and run f1( 5 ); // can complie and runreturn 0;
}