最感性的认识。
当然,左值也是可以在右边的。
左值是可以被修改的,右值不能。
当然取地址也是。
生存周期一般左值会比右值的长,一般右值都计算时产生的无名临时对象,存在时间比较短。
下面还有一种情况也要区分。
左值引用:可以引用一个对象,有时候也可以绑定一个右值。
右值引用:只能引用右值。
看以下代码,比较正常。
int a = 3; int &p1 = a; // 左值引用
若左值引用右值将报错
但加上const就可以引用了
不能把左值绑定到右值,但使用move可以把左值转换右值就可以绑定
示例1
示例2
示例3
理解了上边知识,接下来是引用折叠规则,
先看演示代码
#include <iostream> using namespace std; using lRef = int&; //左值引用 using rRef = int&&; //右值引用 int main(int argc, char **argv) { is_lvalue_reference<lRef &>::value ? cout << "lRef & 左值引用" << endl : cout << "lRef & 右值引用" << endl; is_lvalue_reference<lRef &&>::value ? cout << "lRef && 左值引用" << endl : cout << "lRef && 右值引用" << endl; is_rvalue_reference<rRef &>::value ? cout << "rRef & 右值引用" << endl : cout << "rRef & 左值引用" << endl; is_rvalue_reference<rRef &&>::value ? cout << "rRef && 右值引用" << endl : cout << "rRef && 左值引用" << endl; return 0; }
调试结果
这就是引用折叠规则。
这怎么理解呢,看下图
可以看到只有都是右值引用的时候才是右值引用,当然只有一个右值引用的情况下自然而然也是右值引用。
这是就引用折叠。
主要用于参数转发时是左值传入还是右转入
考虑以下代码
#include <iostream> using namespace std; template<typename T> void Fun1(T& v) { cout << "左值引用调用" << v << endl; } template<typename T> void Fun1(T&& v) { cout << "右值引用调用" << v <<endl; } template<typename T> void Fun(T&& v) { Fun1(v); } int main(int argc, char **argv) { int a = 3; Fun(a); return 0; }
主函数里给Fun传入a,根据上边知识,a是一个左值,看调试结果是调用哪一个重载版本Fun1
结果跟预想的一样,接下来更改换右值传入。
int main(int argc, char **argv) { Fun(5); return 0; }
调试结果
我们发现跟想象中不一样!!!
这时候该怎么办。
c++11中提供了一个用于完美转发的函数forward。
还提供了一个move函数,用于把左值变成右值的方法。
forward会根据引用折叠规则得出传入的是左值引用还是右值引用
接下来只需更改一下Fun函数,其他的不变
template<typename T> void Fun(T&& v) { Fun1(forward<T>(v)); }
调试结果
发现跟我们预想一样了。
完美转发完整示例
#include <iostream> using namespace std; template<typename T> void Fun1(T& v) { cout << "左值引用调用" << v << endl; } template<typename T> void Fun1(T&& v) { cout << "右值引用调用" << v <<endl; } template<typename T> void Fun(T&& v) { Fun1(forward<T>(v)); } int main(int argc, char **argv) { int a = 1; Fun(a); Fun(move(a)); const int b = 2; Fun(b); Fun(move(b)); Fun(5); return 0; }
调试结果
学无止境。
---End