C++后置自增运算符赋值行为解惑:obj1++=obj2为何合法?
关于C++前置/后置自增运算符左值特性的疑问
我正在阅读Deitel的C++教材,书中提到:
后置自增运算符通常返回一个包含自增前对象原始值的临时对象。C++将这类对象视为右值,不能用于赋值操作的左侧。前置自增运算符返回自增后的实际对象及其新值,该对象可作为左值用于后续表达式。
为验证这段内容,我编写了重载前置和后置自增运算符的Counter类测试程序:
#include <iostream> class Counter { private: int count; public: Counter(int initialCount = 0) : count(initialCount) {} Counter& operator++() { ++count; return *this; } Counter operator++(int) { Counter temp(*this); ++count; return temp; } void display() const { std::cout << "Count: " << count << std::endl; } }; int main() { Counter obj1(5); Counter obj2(10); ++obj1 = obj2; std::cout << "After ++obj1 = obj2; we get: " << std::endl; obj1.display(); std::cout << std::endl; obj1++ = obj2; std::cout << "After obj1++ = obj2; we get: " << std::endl; obj1.display(); return 0; }
不同编译器上的输出一致:
After ++obj1 = obj2; we get: Count: 10 After obj1++ = obj2; we get: Count: 11
我的疑问:语句obj1++ = obj2;中,我们是否将obj1++用作了左值?根据教材内容这应该是不允许的,能否解释该行为并澄清此场景下左值与右值的概念?
核心解释
首先明确:你的代码中obj1++ = obj2;确实把后置自增的返回值(临时对象)当作了左值,但这是类类型与内置类型的规则差异导致的——内置类型的后置自增返回右值,绝对不能放在赋值左侧(比如i++ = 10;会直接编译报错),但类类型的右值在C++中允许调用成员函数(包括赋值运算符)。
为什么代码能编译运行?
执行obj1++ = obj2;时,实际步骤是:
- 调用
obj1.operator++(int),创建一个包含obj1自增前值(此时为10)的临时Counter对象; - 调用这个临时对象的默认赋值运算符,将
obj2的值(10)赋给它; - 临时对象在语句结束后立即销毁,完全不影响原
obj1——这就是你看到obj1最终值为11的原因:后置自增已经把obj1的count从10加到11,赋值操作只修改了那个被销毁的临时对象,没有实际意义。
教材内容的正确性
教材的描述针对的是内置类型,以及符合最佳实践的运算符重载:
按照惯例,我们应该让类的后置自增行为和内置类型一致,禁止将其作为左值。可以通过限制赋值运算符只能用于左值来实现:
class Counter { // ... 其他成员 ... // 仅允许左值调用赋值运算符 Counter& operator=(const Counter&) &; };
这样obj1++ = obj2;就会编译报错,和内置类型的行为对齐。
左值与右值的澄清
- 左值:可以取地址、能出现在赋值左侧的表达式,代表持久存在的对象(比如
obj1、++obj1返回的引用)。 - 右值:不能取地址、通常是临时存在的对象或字面量(比如
5、obj1++返回的临时对象)。
类类型的右值虽然可以调用成员函数,但这并不改变它是右值的本质——临时对象会被快速销毁,对它的赋值没有实际价值。
内容的提问来源于stack exchange,提问作者Osmium
相关产品推荐
相关产品推荐

