在映射中分配值时正确的求值顺序是什么?

Mic*_*aut 27 c++ c++14

我知道编译器通常是代码中错误的最后一个责任,但我没有看到对以下 C++ 代码的以下行为的任何其他解释(从实际项目中提炼出来):

#include <iostream>
#include <map>

int main()
{
    auto values = { 1, 3, 5 };
    std::map<int, int> valMap;

    for (auto const & val : values) {
        std::cout << "before assignment: valMap.size() = " << valMap.size();
        valMap[val] = valMap.size();
        std::cout << " -> set valMap[" << val << "] to " << valMap[val] << "\n";
    }
}
Run Code Online (Sandbox Code Playgroud)

该代码的预期输出是:

before assignment: valMap.size() = 0 -> set valMap[1] to 0
before assignment: valMap.size() = 1 -> set valMap[3] to 1
before assignment: valMap.size() = 2 -> set valMap[5] to 2
Run Code Online (Sandbox Code Playgroud)

但是,当我使用(默认)C++14 编译器构建发布版本时,输出变为:

before assignment: valMap.size() = 0 -> set valMap[1] to 1
before assignment: valMap.size() = 1 -> set valMap[3] to 2
before assignment: valMap.size() = 2 -> set valMap[5] to 3
Run Code Online (Sandbox Code Playgroud)

换句话说,中的所有值valMap都比应有的值大 1 - 看起来映射是在评估赋值的右侧之前附加的。

这种情况仅发生在使用 C++14 语言标准的发布版本中(这是 VS2019 中的默认设置)。调试版本工作得很好(我讨厌这种情况发生 - 我花了几个小时才弄清楚发生了什么),C++17 和 C++20 的发布版本也是如此。这就是为什么它对我来说看起来像一个错误。

.size()我的问题是:这是一个编译器错误,还是我在作业中使用 using 做了一些错误/危险的事情?

Ala*_*les 33

A = B在 c++17 之前没有指定 的求值顺序,在 c++17 之后B保证在 之前求值A,参见https://en.cppreference.com/w/cpp/language/eval_order规则 20。

因此,c++14 中未指定的行为valMap[val] = valMap.size();,您应该使用:

auto size = valMap.size();
valMap[val] = size;
Run Code Online (Sandbox Code Playgroud)

emplace或者通过使用比依赖[]自动插入值(如果该值尚不存在)更明确来避免该问题:

valMap.emplace(val, size);
Run Code Online (Sandbox Code Playgroud)

  • 来自更现代的语言,这种认识让我有点困惑,事实上 `valMap[2] = SomeFunctionThatThrows();` 实际上可以使用 &lt; C++17 来改变映射。 (7认同)