我刚刚在我的代码 (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()],并且您得到您期望的顺序。
让我们以更详细的方式写下循环代码:
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)