C++14与C++17中赋值运算符求值顺序差异问题咨询
问题1:求值顺序差异说明
是的,你观察到的差异本质就是赋值运算符左右操作数的求值顺序规则变化:
- C14及更早标准中,赋值运算符
E1 = E2的左右操作数求值顺序是未指定的,编译器可以自由选择先求值左操作数a[0]还是先求值右操作数append()。你遇到的C14环境就是先计算了a[0]的内存地址(拿到重分配前的旧地址),再调用append()触发重分配导致旧地址失效,最终赋值操作写在了已释放的旧内存上,新的vector内存里的a[0]没有被修改,所以输出0。 - C++17之后标准明确规定:简单赋值表达式的右操作数会先完成全部求值(包括所有副作用),再求值左操作数。所以这里会先调用
append()完成扩容,之后再计算a[0]的新内存地址,赋值操作正确写入新地址,输出10。
问题2:标准修改说明
这确实是C17明确的规则修正,对应的提案是P0145R3《Refining Expression Evaluation Order for Idiomatic C》。
之前的标准中多个运算符的操作数求值顺序未指定,很容易因为操作数的副作用导致不可移植的不确定行为,C++17针对这类常见场景明确了求值顺序:除了赋值运算符外,还包括下标运算符、函数调用的参数和函数名求值顺序、移位运算符等的求值顺序都做了明确规定,消除了这类场景的不确定性。
问题3:C++11/14兼容方案
只需要手动拆分赋值语句,强制先完成右值的求值,再执行赋值即可,写法非常简洁:
// 替换原来的 a[0] = append(); int val = append(); a[0] = val;
这种写法的执行顺序是语言标准明确规定的,不受编译器求值顺序选择的影响,在所有C版本中行为都和C17的原赋值语句一致。
内容的提问来源于stack exchange,提问作者Blaz Korecic
相关产品推荐
相关产品推荐

