首页 > 解决方案 > 是否将地图大小作为值插入地图未定义的行为?

问题描述

编辑:这个问题不应该被关闭,如果你看看答案,你会发现它们完全不同(旧问题没有提到 C++17)。

我正在阅读一篇 PVS博客文章,其中提到了以下错误。

(减少)

std::map<int,int> m;
m[7]=5;
auto val = 15;
if (!m.contains(val)){
    m[val] = m.size(); // bug here
}

根据博客文章,这是错误的。我一直认为 map 的 operator [] 调用是一个函数调用,因此 .size() 在 [] 之前排序,因为函数充当序列点。

那么为什么这是一个错误呢?

注意:我知道自 C++11 以来不存在序列点,但我使用它们是因为新的措辞对我来说更难理解。

标签: c++sequence-pointsunspecified-behavior

解决方案


预 C++17

§ 1.9 程序执行 [intro.execution] (n3690 c++14 草案)

  1. 除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值是无序的。

并且 5.17 [expr.ass] 没有提到内置赋值的操作数之间的任何排序。因此,内置赋值运算符的两个操作数的求值=是无序的。

m[val]并且m.size()可以按任何顺序进行评估(甚至可以重叠 - 交错 CPU 指令)。

考虑:

  • m[val]具有修改地图大小的副作用(标量)

  • m.size()访问地图大小的值计算

§ 1.9 程序执行 [intro.execution] (n3690 c++14 草案)

  1. [...] 如果标量对象的副作用相对于 [...] 或使用相同标量对象的值的值计算是无序的,则行为未定义。

所以是的,行为确实是未定义的。

C++17

§8.5.18 赋值和复合赋值运算符 [expr.ass](n4713 C++17 草案)

  1. 赋值运算符 (=) [...] 右操作数在左操作数之前排序。

所以定义了行为。m.size()将在之前进行评估m[val]


推荐阅读