在回答 this question 之后,关于所讨论的代码是否为未定义行为进行了长时间的讨论。这是代码:

std::map<string, size_t> word_count;
word_count["a"] = word_count.count("a") == 0 ? 1 : 2;

首先,众所周知,这至少是未指明的。结果根据首先评估分配的哪一侧而有所不同。在我的回答中,我跟踪了四个结果案例中的每一个,并首先评估了哪一边的因素以及元素是否在此之前存在。

也出现了一个简短的表格:
(x = 0) = (x == 0) ? 1 : 2; //started as
(x = 0) = (y == "a") ? 1 : 2; //changed to

我声称它更像是这样:
(x = 0, x) = (x == 0) ? 1 : 2; //comma sequences x, like [] should

最终,我找到了一个似乎对我有用的例子:
i = (++i,i++,i); //well-defined per SO:Undefined Behaviour and Sequence Points

回到最初,我将其分解为相关的函数调用,以便更容易理解:
operator=(word_count.operator[]("a"), word_count.count("a") == 0 ? 1 : 2);
   ^       inserts element^                        ^reads same element
   |
assigns to element

如果 word_count["a"] 不存在,有人认为它会被分配两次,中间没有排序。如果我认为正确的两件事实际上是:
  • 当挑选侧面时,必须在另一边开始之前进行整体侧。
  • 诸如 word_count["a"] = 1 之类的结构表现出明确定义的行为,即使在插入元素然后分配给的情况下也是如此。

  • 这两个说法是真的吗?最终,这实际上是未定义的行为,如果是,为什么第二个语句起作用(假设它起作用)?如果第二个是假的,我相信世界上所有的 myMap[i]++; 都是不正确的。

    有用的链接:Undefined behavior and sequence points

    最佳答案

    行为未指定,但不是未定义的

    请注意,在表达式中:

    word_count["a"] = word_count.count("a") == 0 ? 1 : 2;
    //              ^
    

    ^ 标记的赋值运算符是 内置的 赋值运算符,因为 std::mapoperator [] 返回一个 size_t&

    根据 C++11 标准关于内置赋值运算符的第 5.17/1 段:



    这意味着在内置赋值中,例如:
    a = b
    

    首先计算操作数(以未指定的顺序),然​​后执行赋值,最后执行整个赋值表达式的值计算。

    考虑原始表达式:
    word_count["a"] = word_count.count("a") == 0 ? 1 : 2;
    //              ^
    

    由于上面引用的段落,在任何情况下都不会对同一对象有两个未排序的赋值: ^ 标记的赋值将始终在 operator [] 执行的赋值之后排序 (作为左侧表达式评估的一部分)如果 map 中不存在 key "a"

    但是,根据首先评估赋值的哪一侧,表达式将具有不同的结果。因此,行为是未指定的,但不是未定义的。

    10-08 11:14