目录
一,引用
1,函数参数引用传递
#include<iostream>
using namespace std;
void f(int& x)
{
x--;
}
int main()
{
int x=3;
f(x);
cout<<x;
return 0;
}
输出2
常见错误:
void f(int& x)
{
x--;
}
int main()
{
f(3);
return 0;
}
2,引用型变量
#include<iostream>
using namespace std;
int main()
{
int x=3;
int& x2=x;
x2++;
cout<<x;
return 0;
}
输出4
常见错误:
int main()
{
int& x = 3;
return 0;
}
3,函数返回引用型
#include<iostream>
using namespace std;
int& f(int arr[],int id)
{
if(id>sizeof(arr)/sizeof(int))id=0;
return arr[id];
}
int main()
{
int arr[]={1,2,3};
f(arr,1)++;
f(arr,10)++;
cout<<arr[0]<<arr[1]<<arr[2];
return 0;
}
输出233
二,左值和右值1,左值和右值
左值就是有内存地址的,可以用&取地址,右值是没有内存地址的,也就不能取址。
首先,等号左边必须是左值,右边可以是左值可以是右值。
int x = 1; // 正确,x是左值,1是右值
1 = x; // 错误
int a = x; // 正确, a是左值
a + 1 = 0; // 错误,a+1是右值
PS:除非对运算符做了重载,否则我们指的都是默认运算符,下同。
其次,字面值是右值,匿名变量是右值。
最后,左值和右值描述的其实是变量是否作为一个对象保存在计算机中,和存储的位置是不是在内存中没有绝对的关系。
比如左值变量用register修饰,虽然保存在寄存器中,但仍然是左值。而如果对register变量取址,那么它一开始就不会被放入寄存器。
2,表达式
有的表达式产生的是左值,有的表达式产生的是右值。
int main()
{
int x = 1, y = 2;
x + 1; // 右值
x + y; // 右值
x++; // 右值
&x; // 右值
int* a; // 左值
*a; // 左值
a + 1; // 右值
*(a + 1); // 左值
return 0;
}
一般匿名变量指的就是表达式产生的右值,但也不绝对,例如*(a + 1)这个匿名变量就是左值。
3,取址和解地址
取址 & 是根据左值得到右值,解地址 * 是根据左值或者右值得到左值。
即取址 & 只能用于左值,而 * 都可以。
4,引用和const
& 作为引用时,是可以用于右值的。
void f(const int &x)
{
cout << x;
}
int main()
{
f(3);
return 0;
}
即,非const的左值引用、const的左值和右值引用都是可以的,非const的右值引用是不行的。
而这居然是可以的:
void f(const int &constant)
{
cout << constant;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
(*modifier)++;
cout << constant;
}
int main()
{
f(3);
return 0;
}
输出34
而如果是这样:
void f(const int &constant)
{
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
(*modifier)++;
}
int main()
{
const int x = 3;
f(x);
cout << x;
return 0;
}
输出3
说明参数相当于是按值传递的。
换个角度看,f(3)其实是右值引用,f(x)是左值引用。
5,move
move函数可以实现把左值换成右值
template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable
return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}
其实也就是把一个变量本身作为返回值,函数的返回值自然就是右值。
6,右值引用
用&&做单目运算符时,表示右值引用。
用Compiler Explorer可以在线编译代码。
下面2个代码的汇编是一样的:
void f(int &&constant)
{
return;
}
int main()
{
f(3);
return 0;
}
void f(const int &constant)
{
return;
}
int main()
{
f(3);
return 0;
}
对于右值,其实只是没有给它取名字而已,和左值变量的主要区别在于内存,而在寄存器处,对于计算来说是一样的。
而右值引用这个新语法,使得我们可以像左值引用一样,直接引用一个变量而不用copy,从而高效引用右值,这就是移动语义、完美转发。
从网上盗了一张图: