一、左值右值
1.1 定义
在C++中有几个晦涩的概念:左值、右值、泛左值、纯右值、将亡值
泛左值=左值+将亡值; 右值=纯右值+将亡值; 我们可以发现将亡值既属于泛左值又属于右值;
这些分类是由于它们具有不同性质而命名的,但其实我们只需要掌握左值和右值的概念就已经足够了。
这5种值类型我总结了一张表格,如下:
首先这几个名词用来形容一个表达式,在C++中,一切表达式都必然属于左值、将亡值、纯右值中的一种。而右值=将亡值+纯右值,所以我们又可以说一切C++表达式都必然要么是左值要么是右值。
从性质上来看:
- 只有左值可以取地址、可以作为等号左边的操作数;不具有该性质的就必然是右值。
- 左值和将亡值都具有多态性质;不具有该性质的就是纯右值了。
1.2 举例
用是否可以取地址来验证表达式是左值还是右值:
#include <utility>#include <string>using namespace std;string &f1(string &a) {return a;}string &&f2() {string a;return move(a);}string f3() {return "";}int main() {string s("hello");printf("%p\\n", &f1(s)); //f1(s)是左值,可以取地址// printf("%p\\n", &f2()); //f2()是将亡值,不能取地址// printf("%p\\n", &f3()); //f3()是纯右值,不能取地址int a = 1;printf("%p\\n", &++a); //前置自增是左值,可以取地址// printf("%p\\n", &a++); 后置自增是右值,不能取地址printf("%p\\n", &"hello"); //字符串字面值是左值,可以取地址// printf("%p\\n", &42); //数字字面值是右值,不能取地址}
验证将亡值的多态性
#include <iostream>struct A {virtual void f() {printf("classA\\n");}};struct B : A {void f() override {printf("classB\\n");}};int main() {static_cast<A&&>(B()).f(); //打印classB, static_cast表达式是将亡值,静态类型为A,动态类型为B,发生多态static_cast<A>(B()).f(); //打印classA, static_cast表达式是纯右值,静态类型为A,动态类型为A,未发生多态}
二、左值引用 右值引用
2.1 定义
在C++中引用类型与表达式之间的绑定关系有语法上的规定,如下表:
- T&只能绑定非常量左值
- const T&是万能引用类型,可以绑定任意值类型的表达式
- T&&只能绑定非常量右值
- const T&&能绑定右值类型的表达式,无论是否常量
代码验证绑定规则:
#include <utility>#include <string>using namespace std;int main() {string s1; //非常量左值const string s2; //常量左值string &r1 = s1; //【非常量左值引用】可以绑定【非常量左值】// string &r2 = s2; //【非常量左值引用】不能绑定【常量左值】const string &r3 = s1; //【常量左值引用】可以绑定【非常量左值】const string &r4 = s2; //【常量左值引用】可以绑定【常量左值】const string &r5 = move(s1); //【常量左值引用】可以绑定【非常量右值】const string &r6 = move(s2); //【常量左值引用】可以绑定【常量右值】string &&r7 = move(s1); //【非常量右值引用】可以绑定【非常量右值】// string &&r8 = move(s2); //【非常量右值引用】不能绑定【常量右值】const string &&r9 = move(s1); //【常量右值引用】可以绑定【非常量右值】const string &&r10 = move(s2); //【常量右值引用】可以绑定【常量右值】}
2.2 引用的作用
左值本身就是具名变量,当它被绑定给左值引用时只是起了个别名,生命周期并不会变化。
而右值本身的生命周期会在表达式结束后立即被销毁,当右值被绑定给引用时,该右值“重获新生”,它的生命周期会延长,与该引用的生命周期相同。
代码举例:
未完待续
1d45f
2.3 需要注意的点
右值引用是个左值。(是不是很绕口?)
准确的来说,所有的引用,无论是左值引用还是右值引用,都是左值表达式。
这是因为右值引用延长了右值的生命周期,我们可以去取右值引用的地址,因此它是个左值。
代码举例:
未完待续