C++左值/右值/左值引用/右值引用

1)C++入门级小知识,分享给将要学习或者正在学习C++开发的同学。

2)内容属于原创,若转载,请说明出处。

3)提供相关问题有偿答疑和支持。

左值和右值的概念:

早期的c语言中关于左值和右值的定义:
左值是可以位于赋值运算符"="左侧的表达式(当然,左值也可以位于"="的右侧);
右值是不可以位于赋值运算符 "="左侧的表达式;

区分左值和右值的一个简单办法是:看能不能对表达式取地址,如果能,则为左值、当然也可以充当右值,如果不能为右值。

如下:

int a;
int b;
a = b;
b = a;
以上a和b都可以位于运算符=的左侧,因此a,b都是左值;

int c;
c = 100; //ok
100 = c; //err
以上常量100只能位于运算符=的右侧因此常量100是右值

不过在 C++ 里面,左值和右值不能这样定义。根据《C++ Primer》的说法,左值和右值可以这样区分:
一个表达式是左值还是右值,取决于我们使用的是它的值还是它在内存中的位置(作为对象的身份)。
也就是说一个表达式具体是左值还是右值,要根据实际在语句中的含义来确定。

如下:
int foo=100;
int bar;

// 将 foo 的值赋给 bar,保存在 bar 对应的内存中
// foo 在这里作为表达式是右值;bar 在这里作为表达式是左值
// 但是 foo 作为对象,既可以充当左值又可以充当右值
bar = foo;

因为 C++ 中的对象本身可以是一个表达式,所以这里有一个重要的原则,即
在大多数情况下,需要右值的地方可以用左值来替代,但需要左值的地方,一定不能用右值来替代。

又有一个重要的特点,即
左值存放在对象中,有持久的状态;而
右值要么是字面常量,要么是在表达式求值过程中创建的临时对象,没有持久的状态

左值引用和右值引用的概念:
左值引用是常见的引用,所以一般在提到「对象的引用」的时候,指得就是左值引用。
如果我们将一个对象的内存空间绑定到另一个变量上,那么这个变量就是左值引用。
在建立引用的时候,我们是将内存空间绑定,因此我们使用的是一个对象在内存中的位置,这是一个左值。
因此,我们不能将一个右值绑定到左值引用上。另一方面,由于常量左值引用保证了我们不能通过引用改变对应内存空间的值,因此我们可以将右值绑定在常量引用上。

如下是左值引用:(左值引用只能绑定到左值上,const引用除外)
int foo=42;
int& bar = foo;  // OK: foo 在此是左值,将它的内存空间与 bar 绑定在一起
int& baz = 42;   // Err: 42 是右值,不能将它绑定在左值引用上
const int& qux = 42;  // OK: 42 是右值,但是编译器可以为它开辟一块内存空间,绑定在 qux 上


c++11中新加了右值引用&&:
对右值进行绑定的引用就是右值引用,他的语法是这样的A&&,通过双引号来表示绑定类型为A的右值。通过&&我们就可以很方便的绑定右值了;
如下是右值引用:(右值引用也是引用,但是它只能且必须绑定在右值上)。
int foo=42;
int& bar = foo;        // OK: 将 foo 绑定在左值引用上
int&& baz = foo;       // Err: foo 可以是左值,所以不能将它绑定在右值引用上
int&& qux = 42;        // OK: 将右值 42 绑定在右值引用上
int&& quux = foo * 1;  // OK: foo * 1 的结果是一个右值,将它绑定在右值引用上
int& garply = foo++;   // Err: 后置自增运算符返回的是右值,不能将它绑定在左值引用上
int&& waldo = foo--;   // OK: 后置自减运算符返回的是右值,将它绑定在右值引用上

由于右值引用只能绑定在右值上,而右值要么是字面常量,要么是临时对象,所以:
右值引用的对象,是临时的,即将被销毁;并且
右值引用的对象,不会在其它地方使用。

我们思考一个问题:右值引用本身是左值还是右值?或者可以先思考一下它的对偶问题:左值引用本身是左值还是右值?

先看下面的代码:
int foo(42);
int& bar = foo;     // bar 是对 foo 的左值引用
int& baz = bar;     // baz 是对 bar 的左值引用,因而 bar 是右值
int qux = ++foo;    // 前置自增运算符返回左值引用,在这里赋值给 qux,此时左值引用作为右值
观察上面代码,不难发现,左值引用本身既可以是左值,又可以是右值。它具体是左值还是右值,依然取决于它作为表达式时候的作用。更仔细地观察可以发现,
如果左值引用作为一个变量被保存下来了,那么它就可以是左值(当然也可以起到右值的作用);而如果左值引用是一个临时变量(例如函数的返回值),那么它就是右值。

同理可以用在右值引用上。

class Type;
void foo(Type&& bar) {
    // 将右值引用作为 Type 的构造函数的参数
    // 此时匹配 Type::Type(const Type& orig), 即拷贝构造函数
    // bar 是左值
    Type baz(bar);
}
Type&& qux();
quux = qux();   // qux 的返回值是 Type 类型的右值引用,此时右值引用是右值
和左值引用一样,右值引用本身也既可以作为左值也可以作为右值。并且,同样的是:如果右值引用作为变量被保存下来了,那么应该把它当做是一个左值看待;否则应当作为右值看待。

因此,不论是左值引用还是右值引用,都有

当引用作为变量被保存下来,那么它是左值;否则
它是右值。

### C++ 左值的概念及区别 在C++中,左值(lvalue)和(rvalue)是表达式分类中的两个重要概念。左值通常表示具有持久存储的实体,可以被取地址并多次使用;而则表示临时对象或字面量,通常不可取地址且生命周期较短。 #### 1. 左值(lvalue) 左值是指能够出现在赋语句侧的表达式,它通常表示一个具有名字的对象,并且可以对其取地址。例如变量名、数组元素等都属于左值[^3]。 ```cpp int a = 10; // 'a' 是左值 int& ref = a; // 引用 'ref' 绑定到左值 'a' ``` #### 2. (rvalue) 是指不能出现在赋语句侧的表达式,通常是临时对象或字面量。不能直接取地址,但在C++11之后引入了引用(rvalue reference),使得我们可以操作这些临时对象[^3]。 ```cpp int b = 20 + 30; // '20 + 30' 是 ``` #### 3. 区别总结 - **持久性**:左值通常表示具有持久存储的对象,而通常是临时对象或字面量。 - **可取地址性**:左值可以取地址,而通常不能取地址。 - **使用场景**:左值可以直接绑定到非const引用,而只能绑定到const引用引用[^4]。 #### 示例代码 以下代码展示了左值的区别以及如何使用引用: ```cpp #include <iostream> using namespace std; void Func(int& x) { cout << "左值引用" << endl; } void Func(const int& x) { cout << "const左值引用" << endl; } void Func(int&& x) { cout << "引用" << endl; } void Func(const int&& x) { cout << "const引用" << endl; } template<typename T> void f(T&& t) { Func(t); // 根据参数t的类型去匹配合适的重载函数 } int main() { int a = 4; // 左值 f(a); // 调用左值引用版本[^1] const int b = 8; // const左值 f(b); // 调用const左值引用版本[^1] f(10); // 调用引用版本[^1] const int c = 13; f(std::move(c)); // 调用const引用版本[^1] return 0; } ``` #### 关于万能引用 万能引用(universal reference)是C++中的一种模板机制,允许模板参数根据上下文自动推导为左值引用引用。例如,在上述代码中,`f`函数的模板参数`T&&`会根据传入的实参类型自动调整为左值引用引用[^2]。 ```cpp template<typename T> void tempFun(T&& t) { t = 40; cout << t << endl; } int main() { int x = 19; tempFun(x); // T为int&, t为int&,即得到左值引用[^2] tempFun(30); // T为int,t为int&&,即得到引用 int&& r = 100; tempFun(r); // 虽然r绑定到一个,但r变量本身是个左值,因此得到左值引用 return 0; } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值