C++引用、左值和右值

发布时间:2026/7/23 17:43:26

C++引用、左值和右值 目录一引用1函数参数引用传递2引用型变量3函数返回引用型二左值和右值1左值和右值2常见的左值和右值1变量、字面量2表达式3else3纯右值、将亡值4取址和解地址5引用和const5move6右值引用7转发引用、完美转发三拷贝构造和移动构造1类默认生成的拷贝构造和移动构造函数2noexcept3vector的移动扩增一引用1函数参数引用传递#includeiostream using namespace std; void f(int x) { x--; } int main() { int x3; f(x); coutx; return 0; }输出2常见错误void f(int x) { x--; } int main() { f(3); return 0; }2引用型变量#includeiostream using namespace std; int main() { int x3; int x2x; x2; coutx; return 0; }输出4常见错误int main() { int x 3; return 0; }3函数返回引用型#includeiostream using namespace std; int f(int arr[],int id) { if(idsizeof(arr)/sizeof(int))id0; return arr[id]; } int main() { int arr[]{1,2,3}; f(arr,1); f(arr,10); coutarr[0]arr[1]arr[2]; return 0; }输出233二左值和右值1左值和右值1左值就是有内存地址的可以用取地址右值是没有内存地址的也就不能取址。2非const的左值可以放在赋值运算符的左边const左值不行右值不行。int x 1; // 正确x是左值1是右值 1 x; // 错误 int a x; // 正确, a是左值 a 1 0; // 错误a1是右值3左值和右值描述的其实是变量是否作为一个对象保存在计算机中和存储的位置是不是在内存中没有绝对的关系。比如左值变量用register修饰虽然保存在寄存器中但仍然是左值。而如果对register变量取址那么它一开始就不会被放入寄存器。4右值分为纯右值、将亡值。2常见的左值和右值1变量、字面量变量是左值const变量是const左值。string字面量是const左值其他字面值是右值。2表达式有的表达式产生的是左值有的表达式产生的是右值。int main() { int x 1, y 2; x 1; // 右值 x y; // 右值 x; //左值引用 x; // 右值 x; // 右值 x1; // 左值引用 coutx; // 左值引用 int* a; *a; // 左值 a 1; // 右值 *(a 1); // 左值 return 0; }3else一般匿名变量指的就是表达式产生的右值但也不绝对例如*(a 1)这个匿名变量就是左值。函数是左值。左值对象的成员是左值。3纯右值、将亡值纯右值字面量、运算表达式、非引用的函数调用将亡值右值引用的函数调用4取址和解地址取址 是根据左值得到右值解地址 * 是根据左值或者右值得到左值。即取址 只能用于左值而 * 都可以。5引用和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_castint*(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_castint*(const_p); (*modifier); } int main() { const int x 3; f(x); cout x; return 0; }输出3说明参数相当于是按值传递的。换个角度看f(3)其实是右值引用f(x)是左值引用。5movemove函数可以实现把左值换成右值template class _Ty _NODISCARD constexpr remove_reference_t_Ty move(_Ty _Arg) noexcept { // forward _Arg as movable return static_castremove_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从而高效引用右值这就是移动语义、完美转发。从网上盗了一张图7转发引用、完美转发1转发引用这个符号在 C 中身兼两职当 独立出现时如 int、MyClass它是右值引用。当 出现在模板推导上下文中如 templatetypename T void f(T arg)它被称为转发引用2完美转发前面提到了完美转发这里再做一个辨析。右值引用是一种数据类型而完美转发是一种编程技巧。具体来说完美转发依赖于转发引用和std::forward三拷贝构造和移动构造1类默认生成的拷贝构造和移动构造函数参考六个默认函数2noexcept析构函数、移动构造函数、移动赋值运算符、swap函数一般不允许抛出异常。3vector的移动扩增vector添加元素时可能会发生空间倍增。发生倍增时可能发生元素移动也可能发生元素拷贝。如果元素类型的移动构造函数有noexcept即保证了不会抛出异常那么就采用移动的方式否则就采用拷贝的方式。

相关新闻