关于C++内置复合赋值E1 op=E2的cppreference解释正确性的技术问询
你发现的这个问题非常关键——cppreference的描述本身是语义等价的表述,但如果忽略了运算符优先级和表达式结构,直接做语法上的替换就会出现偏差。我们来一步步拆解:
核心问题:语义等价 vs 语法替换
cppreference说的“E1 op= E2的行为与E1 = E1 op E2完全相同(除了E1仅求值一次)”,这里的“相同”是语义逻辑上的等价,而非简单的字符串替换。它的真实含义是:
- 先求值E1(仅一次),得到一个可修改的左值;
- 求值E2(独立作为一个完整表达式);
- 计算
(E1的值) op (E2的值); - 将结果赋值给E1对应的对象。
而直接写E1 = E1 op E2时,会因为运算符优先级的问题,导致E2的范围被“截断”,和原复合赋值中的E2不是同一个整体。
你的代码例子详解
我们逐个看你的测试用例:
1. 三目运算符的情况
x = 1; x += (-1)? 2 : 2; // 输出3
复合赋值+=的优先级低于三目运算符?:,所以E2是完整的三目表达式(-1)?2:2,计算结果为2,最终x = 1 + 2 = 3。
x = 1; x = x + (-1)? 2 : 2; // 输出2
这里的优先级顺序是:加法+ > 三目?: > 赋值=。所以表达式被解析为:
x = ((x + (-1)) ? 2 : 2);
先计算x + (-1) = 0,然后三目表达式结果为2,最终x = 2。这和复合赋值的语义完全不同,因为这里的E2在语法上被拆成了(-1),而非完整的三目表达式。
2. 相等运算符的情况
x = 2; x += (-2) == 0; // 输出2
+=优先级低于相等运算符==,E2是完整的(-2)==0,结果为false(即0),所以x = 2 + 0 = 2。
x = 2; x = x + (-2) == 0; // 输出1
优先级顺序:加法+ > 相等== > 赋值=。表达式解析为:
x = ((x + (-2)) == 0);
计算x + (-2) = 0,0 == 0结果为true(即1),最终x = 1。同样是因为E2没有被作为整体处理。
更准确的语义表述
你的猜测完全正确:用(E1) = (E1) op (E2)来描述复合赋值的行为会更严谨。这明确了两点:
- E1是一个独立的左值表达式,不会被其他运算符拆分;
- E2是一个完整的表达式,作为
op的右操作数,而非被优先级更高的运算符截断。
cppreference的描述其实隐含了“E2是一个完整操作数”的前提,但如果不明确括号,很容易让读者误解为简单的语法替换,这就是你产生困惑的原因。
内容的提问来源于stack exchange,提问作者Pleasure

