【问题标题】:Shocked by the strange behavior of unordered_map对 unordered_map 的奇怪行为感到震惊
【发布时间】:2017-06-17 02:15:07
【问题描述】:

这是一段非常简单的代码:

#include <cstdio>
#include <unordered_map>

int main() {  
    std::unordered_map<int, int> m;
    m[1] = m.find(1) == m.end() ? 0 : 1;
    printf("%d\n", m[1]);
    return 0;
}

如果地图不包含1,则赋值m[1]=0;否则m[1]=1。我用不同的gcc 编译器here 尝试了这个。

gcc5.2 总是输出 1,gcc7.1 总是输出 0。

为什么会如此不同?它不应该总是0吗?我无法理解这种行为。编写这样的逻辑最安全的方法是什么?

【问题讨论】:

  • 未定义行为的好例子?? “写这种逻辑最安全的方法是什么?”不要这样做。
  • 所以大家在编程之前都应该阅读完整的C++标准。
  • @LaoMao 不,你应该编写不可否认的可以理解的程序。
  • 建议:将标题更改为更通用的名称,例如:“分配排序顺序是否为未定义行为?”
  • 在 C++ 中,所有函数和运算符的默认规则是“参数评估没有顺序”。它发生在函数调用/运算符本身之前。 C++17 添加了一些 限制。不要在阅读时修改值。

标签: c++ c++14 unordered-map c++17


【解决方案1】:

结果取决于编译器是否支持 C++ 2017。

根据 C++ 2017 标准(5.18 赋值和复合赋值运算符)

1 赋值运算符 (=) 和复合赋值运算符 所有组从右到左。都需要一个可修改的左值作为左值 操作数并返回一个引用左操作数的左值。结果 如果左操作数是位域,则在所有情况下都是位域。在所有 情况下,赋值是在值计算之后排序的 右操作数和左操作数,以及在计算值之前 赋值表达式 右操作数在左操作数之前排序 操作数。。对于不确定顺序的函数调用, 复合赋值的操作是一个单一的评估

另一方面,根据 C++ 2014 标准(5.18 赋值和复合赋值运算符)

1 赋值运算符 (=) 和复合赋值运算符 所有组从右到左。都需要一个可修改的左值作为左值 操作数并返回一个引用左操作数的左值。结果 如果左操作数是位域,则在所有情况下都是位域。在所有 情况下,赋值是在值计算之后排序的 右操作数和左操作数,以及在计算值之前 赋值表达式。关于不确定序列 函数调用,复合赋值的操作是单一的 评估。

如您所见,C++ 2014 标准的引用中没有粗体声明。

所以你不应该依赖左右操作数的计算顺序。

【讨论】:

  • 这是否暗示 x = x &lt; 0 ? 0 : xy = y &gt; 100 ? 1 : 0 在 C++17 之前也是 UB?
  • 他们为什么会是UB? RHS 有哪些对 LHS 有排序依赖性的副作用?
  • @taskinoor 否,因为您评估左侧和右侧的哪个顺序并不重要。实际分配总是在双方都评估之后。原始问题中的问题是,在地图上调用 operator[] 会创建具有默认值的条目(如果它以前不存在),这会更改右侧 find() 调用的结果。
  • @xaxxon 感谢您指出地图的operator[] 造成的差异。
猜你喜欢
  • 1970-01-01
  • 2014-05-09
  • 2021-12-08
  • 2021-10-06
  • 2023-03-18
  • 2017-11-22
  • 1970-01-01
  • 1970-01-01
  • 2016-01-28
相关资源
最近更新 更多