关于左值 右值的一些研究

   中国人的优良传统总是喜欢考一些看起来稀奇古怪但是实际上有没有什么用的一些题目,从而来显示自己出题水平的高超,深以难倒广大考生为乐,就比如这一道坑爹的题目:贴上代码:

 

int i=3;
int a=(i++)+(i++);
a=?

 

      对于这种未定义行为的c语言我是不赞成的,本身的定义都模糊的东西怎么可以拿出来用呢,这不是明显逻辑上的犯抽吗?尽管对这种题目很蛋疼,但是做为一个万年中国应试教育下的孩子,我还是对这种题目打算好好研究下,当然,如果我要是这么研究下去就和我题目的主题不符了,我这里拐了个弯,我先研究了下关于运算符优先级的问题,尽管这个表达式的优先级看起来是那么的一目了然。

我先随机抽取了几个单目运算符的表达式,贴代码:gcc 编译

int a=9;
int *p=&++a;
int tmp=*&a;
int tmp_1=!--++a;
int tmp_2=--++a;
int tmp_4=--a++;
int tmp_3=!++a;

 结果报错,有几条错误如下,错误结果如下:lvalue required as unary ‘&’ operand;lvalue required as decrement operand ,翻译过来就是在自加运算和&一元操作中必须要为左值,很好我们现在终于来到了我们的主题,对于左值和右值的区别,我们老师以前模糊的和我们有过简单的交流,左值就是赋值操作符左边的数,是可以进行修改的,右值是赋值操作符右边的数,是不可以修改的,同样的左值也不一样在赋值操作符的左边,也可以在右边,

  比如 int i=2;

     int j=i;

很明显这么简单的解释根本无法解释上述的错误,左值和右值依旧是拦路虎。

经过网上的资料参考,消化吸收后,真心感谢网上的开源精神,看到这么一句话——赋值操作符、自增/自减操作符 (++/–)、取地址操作符 (&,注意该操作符同时要求操作数不能是bit field,以及不能使用register进行修饰) 等要求其操作数必须是左值。对应我上述代码,颇有隔靴搔痒之嫌。几番研究,恍然大悟。贴code

    int i=0;
    (++i)=5;
    (i++)=5;

  编译错误已出;lvalue required as left operand of assignment 赋值左边的操作数要求是左值,我们首先确定了i是一个左值,进行自增自减当然是可以的不会报错,但是自增表达式的值不是一个左值,那么赋值操作符当然无法通过,要求左边必须是左值,当然这种情况在c++中因为运算符重载的关系已经变过了,我们这里不加以引申,后期如果可以的话我还会加以讨论,我们这里只进行对于C语言的研究。

  我姑且认为自增表达式中我们得到的值应该是i的一份临时拷贝,只不过i++ 与++i 一个是返回i的拷贝然后才进行i的对i这个操作数的加1,一个是加1之后返回这个数的拷贝,都是一份临时的拷贝,我们并不知道这个表达式的值放在内存的什么地方,也无法保证表达式的值一次还会放在他的老地方,所以++是一个返回右值的表达式。

明白了这个关系,那么我们用一些式子来加深对左值和右值的概念

 

 

  

        char cha='a';
	char *p=&cha;
	*p='a';
	char tmp=*p;
	char tmp_1=*p+1;
	char tmp_2=*(p+1);
	*(p+1)='b';
	char tmp_3=(*p)+1;
	char *tmp_4=++p;
	char *tmp_5=p++;
	char tmp_6=*++p;
	*++p='c';
	char tmp_7=*p++;
	*p++='d';
	char tmp_8=++*p;
//++*p='d';
	char tmp_9=(*p)++;
//(*p)++='c';
	char tmp_10=++*++p;
//++*++p='c';
	char **p1=&p;
    char tmp_11=*++*p1;
	*++*p1='c';
//char tmp_12=*++++p;
//*++++p='a';

  

这些式子摘自http://blog.sina.com.cn/s/blog_8af6bbd10100ybid.html 特此声明。明白了这些式子相信对于左右值理解更加深刻。暂时就说这么多,以后关于c++的继续补充。

 

      


<think>好的,用户现在问的是关于左值的问题。我需要先回顾一下C++中的左值的概念,以及它们的区别和应用场景。用户可能希望了解如何区分它们,以及在实际编程中如何使用,特别是引用和移动语义相关的部分。首先,我应该明确左值的定义。左值通常指有持久状态的对象,可以取地址,比如变量、返回左值引用的函数调用等。则是临时的,不能被取地址,比如字面量、临时对象或者返回引用的函数调用。然后,我需要考虑用户可能需要的代码示例,比如如何定义引用,移动构造函数和移动赋运算符的实现,以及完美转发。这些例子能帮助用户理解如何在实际中使用这些概念。接下来,用户的问题可能涉及到C++11引入的移动语义,这可以优化资源管理,避免不必要的拷贝。比如,使用std::move将左值转换为,从而调用移动构造函数而不是拷贝构造函数,提升性能。 另外,完美转发也是一个重要概念,允许函数模板将参数以原有的类别传递给其他函数。这需要使用std::forward和通用引用(引用模板)来实现。我需要确保代码示例清晰,注释详细,解释每个部分的作用。同时,要注意常见的错误,比如在移动后使用源对象,或者错误地使用引用。最后,用户可能对相关的高级主题感兴趣,比如万能引用、移动语义与异常安全的关系,或者如何优化性能。这些问题可以作为后续的问题提出,引导用户深入理解。</think>以下是C++中左值(lvalue)和(rvalue)的详细解析及代码示例: ```cpp #include <iostream> #include <vector> #include <utility> // for std::move // 左值判断工具函数 void check_value_category(int&) { std::cout << "lvalue reference\n"; } void check_value_category(int&&) { std::cout << "rvalue reference\n"; } // 移动语义示例类 class Resource { std::vector<int> data; public: Resource() = default; // 拷贝构造函数(左值版本) Resource(const Resource& other) : data(other.data) { std::cout << "Copy constructor\n"; } // 移动构造函数(版本) Resource(Resource&& other) noexcept : data(std::move(other.data)) { std::cout << "Move constructor\n"; } // 移动赋运算符 Resource& operator=(Resource&& other) noexcept { data = std::move(other.data); std::cout << "Move assignment\n"; return *this; } }; // 完美转发示例 template<typename T> void wrapper(T&& arg) { // 万能引用 process(std::forward<T>(arg)); // 完美转发 } void process(const Resource&) { std::cout << "Processing lvalue\n"; } void process(Resource&&) { std::cout << "Processing rvalue\n"; } int main() { // 基础示例 int a = 10; // a是左值 int&& rref = 42; // 42是 check_value_category(a); // 输出lvalue reference check_value_category(10); // 输出rvalue reference check_value_category(rref); // 输出lvalue reference(具名引用是左值) check_value_category(std::move(a)); // 输出rvalue reference // 移动语义示例 Resource res1; Resource res2 = res1; // 调用拷贝构造函数 Resource res3 = std::move(res1); // 调用移动构造函数 // 完美转发示例 Resource res4; wrapper(res4); // 转发左值 wrapper(Resource()); // 转发 } ``` **关键概念解释**: 1. **左值(lvalue)**: - 有持久状态的对象 - 可以取地址(如变量、函数返回的左值引用) - 可以出现在赋运算符的左侧 2. **(rvalue)**: - 临时对象或即将销毁的对象 - 不能取地址(如字面量、临时对象、函数返回的引用) - 主要分为纯(prvalue)和将亡(xvalue) 3. **引用**: - 使用`&&`声明(如`int&& rref`) - 只能绑定到(但具名引用本身是左值) - 主要用途:实现移动语义和完美转发 4. **移动语义**: - 通过`std::move`将左值转换为引用 - 避免不必要的拷贝操作(如大对象的传递) - 需要实现移动构造函数和移动赋运算符 5. **完美转发**: - 保持参数原始的类别(左值/) - 使用`std::forward<T>`和万能引用(`T&&`) - 常用于泛型编程和工厂模式 **典型应用场景**: 1. 优化容器操作(如`vector::push_back`的重载) 2. 实现资源管理类(如智能指针) 3. 构建高性能字符串处理(如string的移动操作) 4. 实现线程安全的数据转移
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值