迭代std :: map vs std :: unordered_map的值类型的区别

时间:2015-03-12 12:14:16

标签: c++ c++11 stl

以下程序迭代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;
}

ideone

运行此程序打印出来:

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> itemMapconst pair<int, Item>,我们会得到相同的结果,这无关紧要:

const pair<const int, Item>

即使best val: 3 best item: 3的{​​{1}}仍为std::pair<const Key, T>。但为什么呢?

2 个答案:

答案 0 :(得分:5)

问题归结为&#34;为什么未定义的行为会为不同的容器提供不同的结果?&#34;,答案是&#34;因为它未定义&#34;。

不那么轻浮:你可能会看到最后一次迭代的价值,这对于有序map来说是最大的,但可能是unordered_map中的任何一个。 / p>

根据您的描述,我想您明白为什么它未定义:const引用可以绑定到临时引用,因此如果类型不完全匹配,它将执行此操作而不是绑定到地图元素本身。引用,因此临时,在循环中作用域,因此在外部不可用。你指向它的悬空指针将最终指向那些重用了那个内存的东西 - 在这种情况下,这很可能是循环的后续迭代中的同一个变量。

正如您在评论中所说,使用auto &&(或者更好,auto const &),以便类型推断确保类型匹配,并且引用直接绑定到地图元素。然后指针将指向一个map元素,并且在循环之外仍然有效。

答案 1 :(得分:4)

您的测试(unordered_mapmap)都表现出未定义的行为。事实上,一个似乎工作而另一个不是偶然的,可能是因为它们以不同的顺序迭代。在地图中放置不同的值也会导致std::map失败。

行为未定义的原因:正如您所说,item的类型与地图的value_type不同。因此,创建了pair<int, Item>类型的临时表,并且item(这是一个const引用)绑定到此临时表。临时的生命周期延长以匹配item的生命周期。

您的bestItem指针指向此临时值,但是当您访问它时,其生命周期已过期,因此行为未定义。

如果item属于匹配类型(const pair<const int, Item>&),则不需要创建临时值,因为它可以直接引用地图value_type。因此bestItem是指向地图的指针,一切正常。