以下程序迭代unordered_map
试图找到最佳元素,但并未完全返回预期结果:
#include <iostream>
#include <unordered_map>
using namespace std;
struct Item
{
int val;
};
int main() {
unordered_map<int, Item> itemMap;
itemMap[0] = {0};
itemMap[1] = {1};
itemMap[2] = {2};
itemMap[3] = {3};
const Item* bestItem = nullptr;
int bestVal = -1;
for (const pair<int, Item>& item : itemMap)
{
if (item.second.val > bestVal)
{
bestVal = item.second.val;
bestItem = &item.second;
}
}
cout << "best val: " << bestVal << " best item: " << bestItem->val;
return 0;
}
运行此程序打印出来:
best val: 3 best item: 0
这似乎是因为value_type
的{{1}}为std::pair<const Key, T>
,但我们正在迭代unordered_map
。果然将此更改为const pair<int, Item>
会导致:
const pair<const int, Item>
但是,如果我们将best val: 3 best item: 3
的类型更改为itemMap
:
std::map
如果我们迭代map<int, Item> itemMap
或const pair<int, Item>
,我们会得到相同的结果,这无关紧要:
const pair<const int, Item>
即使best val: 3 best item: 3
的{{1}}仍为std::pair<const Key, T>
。但为什么呢?
答案 0 :(得分:5)
问题归结为&#34;为什么未定义的行为会为不同的容器提供不同的结果?&#34;,答案是&#34;因为它未定义&#34;。
不那么轻浮:你可能会看到最后一次迭代的价值,这对于有序map
来说是最大的,但可能是unordered_map
中的任何一个。 / p>
根据您的描述,我想您明白为什么它未定义:const
引用可以绑定到临时引用,因此如果类型不完全匹配,它将执行此操作而不是绑定到地图元素本身。引用,因此临时,在循环中作用域,因此在外部不可用。你指向它的悬空指针将最终指向那些重用了那个内存的东西 - 在这种情况下,这很可能是循环的后续迭代中的同一个变量。
正如您在评论中所说,使用auto &&
(或者更好,auto const &
),以便类型推断确保类型匹配,并且引用直接绑定到地图元素。然后指针将指向一个map元素,并且在循环之外仍然有效。
答案 1 :(得分:4)
您的测试(unordered_map
和map
)都表现出未定义的行为。事实上,一个似乎工作而另一个不是偶然的,可能是因为它们以不同的顺序迭代。在地图中放置不同的值也会导致std::map
失败。
行为未定义的原因:正如您所说,item
的类型与地图的value_type
不同。因此,创建了pair<int, Item>
类型的临时表,并且item
(这是一个const引用)绑定到此临时表。临时的生命周期延长以匹配item
的生命周期。
您的bestItem
指针指向此临时值,但是当您访问它时,其生命周期已过期,因此行为未定义。
如果item
属于匹配类型(const pair<const int, Item>&
),则不需要创建临时值,因为它可以直接引用地图value_type
。因此bestItem
是指向地图的指针,一切正常。