目录
传统艺能😎
小编是双非本科大一菜鸟不赘述,欢迎米娜桑来指点江山哦(QQ:1319365055)
🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,打码路上一路向北,彼岸之前皆是疾苦
一个人的单打独斗不如一群人的砥砺前行
这是我和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我
左值与右值🤔
在 C++11 里,尤其突出了两个概念那就是左右值, = 左边就是左值, = 右边就是右值,但是其实左右值的药还藏的蛮深的,里面学问大着呢,记得之前 21 年字节的青训营就出过关于左右值的作业,叫手撕一个完美转发,一时半会儿磕不明白左右值搞混了就直接头疼。
左值是一个表示数据的表达式,如变量名或解引用的指针。 \color{red} {左值是一个表示数据的表达式,如变量名或解引用的指针。} 左值是一个表示数据的表达式,如变量名或解引用的指针。
左值可以被取地址,也可以被修改(const修饰的左值除外)。左值可以出现在赋值符号的左边,也可以出现在赋值符号的右边:
int main()
{
//下面 a、b、c、*a 都是左值
int* a = new int(0);
int b = 1;
const int c = 2;
return 0;
}
右值也是一个表示数据的表达式,如字母常量、表达式的返回值、函数的返回值(不能是左值引用返回)等等 \color{red} {右值也是一个表示数据的表达式,如字母常量、表达式的返回值、函数的返回值(不能是左值引用返回)等等} 右值也是一个表示数据的表达式,如字母常量、表达式的返回值、函数的返回值(不能是左值引用返回)等等
右值不能被取地址,也不能被修改。右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边:
int main()
{
double x = 1.1, y = 2.2;
//常见右值
10;
x + y;
fmin(x, y);
//错误示例(右值不能在赋值符号左边)
//10 = 1;
//x + y = 1;
//fmin(x, y) = 1;
return 0;
}
右值本质就是一个临时变量或常量值,比如代码中的10就是常量值,表达式 x+y 和函数 fmin 的返回值就是临时变量,这些都叫做右值。
这些临时变量和常量值并没有被实际存储起来,这也就是为什么右值不能被取地址的原因,因为只有被存储起来后才有地址。
但需要注意的是,这里说函数的返回值是右值,指的是传值返回的函数,因为传值返回的函数在返回对象时返回的是对象的拷贝,这个拷贝出来的对象就是一个临时变量
对于左值引用返回的函数来说,这些函数返回的就是左值,比如 string 类实现的 [] 运算符重载函数:
namespace str
{
//模拟实现string类
class string
{
public:
//[]运算符重载(可读可写)
char& operator[](size_t i)
{
assert(i < _size); //检测下标的合法性
return _str[i]; //返回对应字符
}
//...
private:
char* _str; //存储字符串
size_t _size; //记录字符串当前的有效长度
//...
};
}
int main()
{
str::string s("hello");
s[3] = 'x'; //引用返回,支持外部修改
return 0;
}
这里的 [] 运算符重载函数返回的是引用,因为它需要支持外部对该位置的修改,所以必须采用左值引用返回。之所以说这里返回的是一个左值,是因为这个返回的字符是被存储起来了的,存在 string 的 _str 对象中,因此这个字符可以被取到地址
左/右值引用🤔
C++98 中就有引用的语法,而 C++11 中新增了右值引用,为了进行区分,于是将 C++11 之前的引用就叫做左值引用,但是无论左值引用还是右值引用,本质都是给对象取别名
左值引用通过 & 来声明。:
int main()
{
//下面 a、b、c、*a 都是左值
int* a = new int(0);
int b = 1;
const int c = 2;
//对上面左值的左值引用
int*& ra = a;
int& rb = b;
const int& rc = c;
int& va = *a;
return 0;
}
右值引用通过 && 来声明:
int main()
{
double x = 1.1, y = 2.2;
//常见的右值
10;
x + y;
fmin(x, y);
//对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double rr3 = fmin(x, y);
return 0;
}
需要注意的是,右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,这时这个右值可以被取到地址,并且可以被修改,如果不想让被引用的右值被修改,可以用const修饰右值引用
int main()
{
double x = 1.1, y = 2.2;
int&& rr1 = 10;
const double&& rr2 = x + y;
rr1 = 20;
rr2 = 5.5; //报错
return 0;
}
那么问题来了,左值引用可以引用右值吗?右值引用又可以引用左值吗?
左值引用不能引用右值,因为右值是不能被修改的,而左值引用是可以修改,这是典型的权限放大问题,但是 const 的左值引用可以引用右值,因为 const 能够保证被引用的数据不会被修改,维持了权限。
template<class T>
//const 左值引用引用右值
void