【问题标题】:Is inserting map size as value into a map undefined behavior? [duplicate]是否将地图大小作为值插入到地图未定义的行为中? [复制]
【发布时间】:2020-05-01 16:18:54
【问题描述】:

编辑:此问题不应该被关闭,如果您查看答案,您会发现它们完全不同(旧问题未提及 C++17)。

我正在阅读一篇 PVS blog 帖子,其中提到了以下错误。

(减少)

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 以来不存在序列点,但我使用它们是因为新的措辞对我来说更难理解。

【问题讨论】:

  • 在@cigien 提供的链接中this particular answer 对序列点进行了梳理。
  • 这在我看来是 PVS 博文中的一个错误。排序可能未指定,但这与 UB 不同。
  • “我一直认为map的operator []调用是一个函数调用,所以.size()在[]之前排序,因为函数充当序列点” ints的赋值虽然没有
  • 这种行为最近发生了变化。在 C++17 之前,这是未指定的行为。在 C++17 中,保证左侧在右侧之前排序,使其定义行为。我对可能的欺骗目标发表了评论,要求更新已接受的答案,以便对您来说是完整的。
  • @s3cur3 如果m[val]m.size() 的序列不确定,那么你是对的。但它们是未排序的,它是 UB

标签: c++ sequence-points unspecified-behavior


【解决方案1】:

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] 之前评估

【讨论】:

  • 我说的是来自m[val] = m.size()m.size()
  • 是的,我的错...顺便说一句 gcc 将行为从 6.4 更改为 7.1 godbolt.org/z/LHU9X3
猜你喜欢
  • 1970-01-01
  • 2014-01-15
  • 1970-01-01
  • 2017-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
相关资源
最近更新 更多