将自己的大小插入 std::map

Pet*_*nen 3 c++ stdmap c++14

我刚刚在我的代码 (C++14) 中遇到了一个奇怪的错误,这是由 std::map 的意外(至少对我而言)行为引起的。这是一个演示行为的简单示例:

#include <iostream>
#include <map>

int main()
{
    std::map<int, int> m;
    for(int i = 0; i < 3; ++i) {
        m[m.size()] = m.size();
    }
    for(const std::pair<int, int>& e : m) {
        std::cout << e.first << " " << e.second << std::endl;
    }
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这打印:

0 1                                                                                                                                                                            
1 2                                                                                                                                                                            
2 3
Run Code Online (Sandbox Code Playgroud)

我期待:

0 0
1 1
2 2
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?地图首先添加一个带有firstset的新元素,然后才(当地图的大小已经增加时)设置second? 我不太明白为什么这会有意义。或者还有其他解释吗?谢谢!

Nat*_*ica 10

表达式中发生了一些事情

m[m.size()] = m.size();
Run Code Online (Sandbox Code Playgroud)

首先,m[m.size()]= m.size()需要进行评估。在 C++14 中,求值顺序是不确定的。 m[m.size()]可能先发生,也可能第二次发生。如果它首先发生,那么您会看到收到的结果。如果它发生在第二个,那么你会得到你期望的输出。

如果你想

0 0
1 1
2 2
Run Code Online (Sandbox Code Playgroud)

然后你需要保证自己订购。你可以map::insert()用来做到这一点:

m.insert(m.size(), m.size());
Run Code Online (Sandbox Code Playgroud)

从 C++17 开始,情况不再如此。 标准改为:

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

它现在保证= m.size()在之前发生m[m.size()],并且您得到您期望的顺序。


fab*_*ian 5

让我们以更详细的方式写下循环代码:

for(int i = 0; i < 3; ++i) {
    int& temp = m.operator[](m.size());
    temp = m.size();
}
Run Code Online (Sandbox Code Playgroud)

请注意,operator[]在为它分配值之前,调用已经插入了一个默认的初始化元素。因此,在评估分配的右侧时,地图已经增长。

要解决此问题,请确保在使用之前确定大小operator[]

for(int i = 0; i < 3; ++i) {
    size_t v = m.size();
    m[v] = v;
}
Run Code Online (Sandbox Code Playgroud)