C++中的左值和右值
左右值的概念
C++中左值(lvalue)和右值(rvalue)在C++11后变得尤为重要,是理解 move、forward等新语义的基础。
左值和右值这两个概念是从C中传承过来的。
左值指的是:既能出现在等号左边,也能出现在等号右边的变量/表达式;
右值指的是:只能出现在等号右边的变量/表达式。
如:
int k;
k = 3;
------
int a;
int b;a = 3;
b = 4;
a = b;
b = a;
------
// 以下写法不合法
3 = a;
a+b = 4;
变量k就是左值,常量3就是右值。
在 c 语言中,通常来说有名字的变量就是左值 (如上面例子中的 a, b),而由运算操作 (加减乘除,函数调用返回值等) 所产生的中间结果 (没有名字) 就是右值,如上的 3 + 4, a + b 等。我们暂且可以认为:左值就是在程序中能够寻值的东西,右值就是没法取到它的地址的东西 (不完全准确),
但如上概念到了 c++ 中,就变得稍有不同。具体来说,在 c++ 中,每一个表达式都会产生一个左值,或者右值,相应的,该表达式也就被称作 “左值表达式", "右值表达式"。对于基本数据类型(primitive types) 来说,左值右值的概念和 c 没有太多不同,不同的地方在于自定义的类型,而且这种不同容易让人混淆:
(1)对于基础类型,右值是不可被修改的 (non-modifiable),也不可被 const, volatile 所修饰 (cv-qualitification ignored)
(2)对于自定义的类型 (user-defined types),右值却允许通过它的成员函数进行修改。
对于(1),这和 c 是一致的,(2)却是 C++ 中所独有。
因此,如果你看到 C++ 中如下的写法,千万不要惊讶:
class cs
{public:cs(int i): i_(i) { cout << "cs(" << i <<") constructor!" << endl; }~cs() { cout << "cs destructor,i(" << i_ << ")" << endl; }cs& operator=(const cs& other){i_ = other.i_;cout << "cs operator=()" << endl;return *this;}int get_i() const { return i_; }void change(int i) { i_ = i; }private:int i_;
};cs get_cs()
{static int i = 0;return cs(i++);
}int main()
{// 合法(get_cs() = cs(2)).change(323);get_cs() = cs(2);// operator=()get_cs().change(32);return 0;
}
这个特性看起来多少有些奇怪,因为通常来说,自定义类型应该设计得和内置类型尽量一样 (所谓 value type,value semantic),但允许成员函数改变右值这个特性却有意无意使得自定义类型特殊化了。对此,我们其实可以这样想,也许会好理解点:自定义类型允许有成员函数,而通过右值调用成员函数是被允许的,但成员函数有可能不是 const 类型,因此通过调用右值的成员函数,也就可能会修改了该右值。
左右值的性质及区别
左值是可寻址的变量/表达式,有持久性;
右值一般是不可寻址的变量/表达式,亦或是表达式求值中创建的无名临时对象,短暂性的。但是在C++11中,自定义类型允许有成员函数,通过右值可以调用成员函数,从而改变右值。
左值和右值的主要区别之一就是C语言中(左值可以被修改,而右值不能);C++11中(左值可以被修改,而右值一般不能,除非自定义类型,右值调用成员函数从而改变)。
此外,右值不能当成左值使用,但左值可以当成右值使用。
左右值引用
左值引用:引用一个对象;
右值引用:就是必须绑到右值的引用,C++11中,右值引用可以实现 “移动语义”,通过&&获得右值引用。
int x = 6; // x是左值,6是右值
int &y = x; // 左值引用,y引用xint &a = x * 6; //
const int &b = x * 6;int &&c = x*6;
int &&d = x;
关于右值,在 c++11 以前有一个十分值得关注的语言的特性:右值能被 const 类型的引用所指向,所以如下代码是合法的。
const cs& ref = get_cs();
而且准确地说,右值只能被 const 类型的 reference 所指向,非 const 的引用则是非法的:
// error
cs& ref = get_cs();
当一个右值被 const 引用指向时,它的生命周期就被延长了,这个用法我在前面一篇博客里讲到过它的相关应用。其中暗藏的逻辑其实就是:右值不能当成左值使用 (但左值可以当成右值使用)。另外值得注意的是,对于前面提到的右值的两个特性:
1) 允许调用成员函数。
2) 只能被 const reference 指向。
它们导致了一些比较有意思的结果,比如:
void func(cs& c)
{cout << "c:" << c.get_i() << endl;
}//error
func(get_cs());//正确
func(get_cs() = get_cs());
其中:func(get_cs() = get_cs()); 能够被正常编译执行的原因就在于,cs 的成员函数 operator=() 返回的是 cs&!不允许非 const reference 引用 rvalue 并不是完美的,它事实上也引起了一些问题,比如说拷贝构造函数的接口不一致了,这是什么意思呢?
class cs
{public: cs& operator=(const cs& c);
};// 另一种写法
class cs2
{public: cs2& operator=(cs2& c);
};
上面两种写法的不同之处就在于参数,一个是 const reference,一个是非 const。对于自定义类型的参数,通常来说,如果函数不需要修改传进来的参数,我们往往就按 const reference 的写法,但对于 copy constructor 来说,它经常是需要修改参数的值,比如 auto_ptr。
// 类似auto_ptr
class auto_ptr
{public:auto_ptr(auto_tr& p){ptr_ = p.ptr_;p.ptr_ = NULL;}private:void* ptr_;
};
所以,对于 auto_ptr 来说,它的 copy constructor 的参数类型是 non const reference。有些情况下,这种写法应该被鼓励,毕竟 non const reference 比 const reference 更能灵活应对各种情况,从而保持一致的接口类型,当然也有代价,参数的语义表达不准确了。除此更大的问题是如果拷贝构造函数写成这样子,却又对 rvalue 的使用带来了极大的不变,如前面所讲的例子,rvalue 不能被 non const reference 所引用,所以像 auto_ptr 的这样的类的 copy constructor 就不能接受 rvalue.
// 错误
auto_ptr p(get_ptr());// operator=() 同理,错误。
auto_ptr p = get_ptr();
这也是 auto_ptr 很不好用的原因之一,为了解决这个问题,c++11 中引入了一种新的引用类型,该种引用是专门用来指向 rvalue 的,有了这种新类型,对 lvalue 和 rvalue 的引用就能明确区分开来了。因为有了这种新的类型,接着就引出了 c++11 中新的语义,move(), forward() 等,这儿先卖个关子,我们下次再讲。
参考c++中的左值与右值 - twoon - 博客园
C++中的左值和右值相关推荐
- c++中的左值与右值
转载自 http://www.cnblogs.com/catch/p/3500678.html 左值 (lvalue)和右值 (rvalue) 是 c/c++ 中一个比较晦涩基础的概念,有的人可能甚至 ...
- c 表达式必须是可修改的左值_C++中的左值,右值,左值引用,右值引用
童帅 2020-2-22 文中的"表达式"都是指赋值表达式 左值,右值,左值引用,右值引用 到底是什么 左值和右值 int a = 10; int b = 5; int c = a ...
- java中的左值右值_快速了解C/C++的左值和右值
最近在segmentfault上看到一个提问<c++隐式的类类型转换问题>:一时不知怎么回答,查阅相关资料后整理了本文,以供参考学习. 定义 早期的C给出的定义:左值是一个表达式,可能出现 ...
- [c++]-c++中的左值和右值、左值引用和右值引用、万能引用和引用折叠及完美转发
1.左值和右值 1.1左值和右值定义 在c++中,左值是一个指向内存的东西,换句话来讲,左值有地址,保存在内存中,右值则为不指向任何地方东西,即不在内存中占有确定位置.一般来说,右值是暂时和短暂的,而 ...
- 详解C++中地左值、右值和移动
左值持久,右值短暂 C++ primer中提到过:当一个对象被用作右值时用的是对象的值(内容),当对象被用做左值时用的是对象的身份(在内存中的位置) [ p r i m e r ] ^{[primer ...
- 简单聊聊C/C++中的左值和右值
文章目录 前言 问题 历史渊源 认识左值和右值 具体的示例 最简单的赋值语句 自增自减运算 前置自增 后置自增 自增表达式赋值 函数表达式 总结 前言 为什么标题要写成简单聊聊,而不是写成什么&quo ...
- C++中 左值 与 右值 的区别
C++中左值与右值的区别 C++的表达式要不然是 右值,要不然是 左值.这两个名词是从C语言继承过来的,原本是为了帮助记忆:左值可以位于赋值语句的左侧,右值则不能. 我们在 C/C++ 编程中并不会经 ...
- 理解C和C++中的左值和右值
翻译至https://eli.thegreenplace.net/2011/12/15/understanding-lvalues-and-rvalues-in-c-and-c/ C/C++编程中不是 ...
- 理解C++中的左值和右值
一.前言 一直以来,我都对C++中左值(lvalue)和右值(lvalue)的概念模糊不清.我认为是时候好好理解他们了,因为这些概念随着C++语言的进化变得越来越重要. 二.左值和右值--一个友好的定 ...
最新文章
- python怎么玩pdf_最全总结!聊聊 python 操作PDF的几种方法
- system.DateTime ToDateTime(System.String)”,因此该方法无法转换为存储表达式-解决方法...
- 工作进度总结汇报01
- 算法-动态规划(01背包)
- python 中的__getattr__和__setattr__
- IOS数据存储 —— 1 沙盒(数据存储目录)
- java的循环控制结构有哪些_java中的控制结构(if,循环)详解
- svn迁移到git_SVN到GIT的迁移完整教程
- 大数据思维与技术——中国大学MOOC课程笔记
- Newton形式的Hermite插值多项式
- RAC修改IP(public/virtual/scan)
- CCS6.1最后安装出现报错如下
- websphereJDBC提供驱动程序配置问题,导致报错:Illegal attempt to enlist multiple 1PC XAResources
- 教你解决主板过热导致的频繁死机
- 乐观锁和悲观锁的含义-实现方式-应用场景
- 42步进电机转速力矩曲线_步进电机的转速 – 转矩曲线
- 【Ceph】Ceph Client
- 实现在线播放Wav音频文件,支持IE和Google
- 33、网络地址转换(NAT)
- 一文看懂网络安全五年之巨变