Visual Studio 2022 ARM预览版后缀自增++疑似编译器Bug问询
问题分析:ARM版MSVC中后缀自增在if判断中的异常行为
结论
这不是未定义行为(UB),属于Visual Studio 2022 ARM预览版(17.4.0 Preview 5.0)的编译器Bug。
为什么不是未定义行为?
- C++标准明确规定,后缀自增运算符
counter++的语义是:先返回变量的当前值,再对变量执行加1操作。 - 表达式
counter++ % 2 == 0的求值顺序完全合法:- 先获取
counter的当前值作为counter++的结果 - 用该结果对2取模
- 将取模结果与0比较
- 先获取
- 整个表达式中没有对
counter的其他未排序访问,不存在违反序列点规则的情况,完全符合标准要求。 - 对比
foo函数的正常运行结果,也能证明该表达式的语义本身是正确的,只是特定场景下的编译优化出现了错误。
为什么判定为编译器Bug?
- 异常仅出现在Release构建中,Debug构建无问题:Debug模式下编译器基本不做优化,而Release模式的优化逻辑错误地改变了
counter++副作用的执行时机,导致第一次判断时错误使用了自增后的值。 - 将
counter++移到if分支外部后问题消失,说明编译器在处理包含push_back的分支时,错误地调整了自增操作的执行顺序,破坏了后缀自增的语义。
复现代码
#include <utility> #include <vector> #include <cstdio> using IntVector = std::vector<int>; std::pair<IntVector, IntVector> my_unweave(const IntVector& xs) { std::pair<IntVector, IntVector> result; std::size_t counter = 0; for (const auto& x : xs) { if (counter++ % 2 == 0) // 该行在MSVC 2022 ARM预览版Release构建中异常 { // 输出顺序为OOODD / EEEVEN / OOODD / EEEVEN printf("EEEVEN\n"); // 后缀自增的语义未被正确执行 result.first.push_back(x); } else { printf("OOODD\n"); result.second.push_back(x); } // 将++counter移到此处可解决问题 // ++counter; } return result; } void foo(const IntVector& xs) { // 该版本运行正常 std::size_t counter = 0; for (const auto& x : xs) { if (counter++ % 2 == 0) printf("Even\n"); else printf("Odd\n"); } } int main() { foo(IntVector({ 0, 1, 2, 3 })); auto u = my_unweave(IntVector({ 0, 1, 2, 3 })); }
运行输出
Even // foo() 工作正常 Odd Even Odd OOODD // my_unweave() 出现顺序反转 EEEVEN // 首次判断就错误进入else分支 OOODD EEEVEN
内容的提问来源于stack exchange,提问作者Pascal T.
相关产品推荐
相关产品推荐

