【问题标题】:Why can't I replace std::map with std::unordered_map为什么我不能用 std::unordered_map 替换 std::map
【发布时间】:2012-01-17 03:54:54
【问题描述】:

这个问题可能有点粗略,因为我家里没有可用的代码,但我知道这个问题否则会困扰我整个周末。

当我尝试将一些代码更新为 C++11 时,我开始将一些 std::map 替换为 std::unordered_map。该代码仅使用std::map::find() 访问地图中的特定元素,所以我认为替换应该很容易。返回的迭代器存储在auto-typed 变量(auto res = map.find( x ),所以打字应该检查正常。但是当使用res->second.do_stuff() 访问存储的元素时,我得到一个编译器错误,告诉我struct std::pair<char, B> does not have a member second。现在这真的让我很困惑,但不幸的是我没有时间进一步调查。

也许这是足够的信息,所以有人可以给我一个关于这个奇怪的编译器错误的提示。或者我的理解是std::mapstd::unordered_map除了需要订购的部件外应该有相同的接口,不正确?

编辑

正如这里所承诺的,对问题进行了更多分析。这很可能会让有人现在更好地帮助我。正如我从 cmets 中的提示猜测的那样,这并不是真正由我访问地图中的元素的点引起的,而是由代码的其他部分引起的。我发现的原因是,我使用 X 类中的映射来存储指向 X 类其他元素的指针(一种树结构)。然而,这似乎适用于std::map,但不适用于std::unordered_map。下面是一些非常简单的代码来展示这个问题:

#include <stdint.h>
#include <unordered_map>
#include <map>

class Test {
  std::map<uint32_t, Test> m_map1; // Works
  std::unordered_map<uint32_t, Test> m_map; // gives error: ‘std::pair<_T1, _T2>::second’ has incomplete type
};

int main() {
  return 1;
}

std::map 有效 std::unordered_map 无效。任何想法为什么会这样,或者可以做些什么来让它与std::unordered_map一起工作?

【问题讨论】:

  • 界面大致相同,但您需要确保您的密钥类型是可散列的,而不仅仅是可比较的。
  • @ildjarn:键是示例中给出的简单字符,因此它们应该很容易散列。此外,问题没有出现在std::unordered_map&lt;char, B&gt; map 的定义中或我在其上调用find( x ) 的位置,而是出现在我尝试访问迭代器(或更准确地说是成员second)的位置。这就是让我感到困惑的部分。
  • 你能举出一个最小的例子来说明这个问题吗?
  • 你的意思是它不起作用? std::unordered_map:ideone.com/P2XUastd::map:ideone.com/etuCH
  • 您的代码在我的 MSVC 2010 上编译得很好。

标签: c++ stl c++11 unordered-map


【解决方案1】:

我猜是因为 std::unordered_map 需要重新散列,因此需要复制元素,所以类型需要是完整的,而只使用指向元素的指针的映射不会出现这个问题。

这里的解决方案是有一个指向指针的无序映射:

std::unordered_map<uint32_t, std::shared_ptr<Test> >. 

【讨论】:

  • 是的,完全正确。不幸的是我还没有时间,自己写一个更详细的解释,所以我会让你有信用;-)。在这一点上要注意的另一件事是,这两行都是无效的。正如drdobbs.com/184403814 中所解释的那样,STL-Collections 绝不允许不完整的类型,因此代码之前是无效的,但编译器从未发现这一点。这可能是其中一种情况,未定义的行为通常不会伤害您,但您不能确定。至少现在我们是安全的。
【解决方案2】:

mapunordered_map 与不完整类型一起使用涉及未定义行为:

特别是在以下情况下效果是不确定的:

[...]

— 如果在实例化模板组件时将不完整类型 (3.9) 用作模板参数,除非该组件特别允许。

【讨论】:

  • 那句话出自哪里?
  • @RalphTandetzky:来自 C++ 标准的标准库要求部分。我认为它是从 C++98 开始的。
  • 如果编译器可以生成良好的错误消息并且在这种情况下不生成工作代码(不完整类型的容器),那就太好了。我们无法确保我们的代码明天可以在另一个编译器/版本上运行。
  • @Renaud:这是关于未定义行为是好是坏的更一般性讨论的一部分。有时您并不关心将代码移植到其他平台,但是利用您的实现知识做一些事情是有用的。例如。有用的struct Tree { std::list&lt;Tree&gt; children; }; 可以在我的工具链上正常工作,但如果按照你说的那样做就会失败。另一个例子是指向非 POD 的指针,我知道它们的内存布局是相同的,但在技术上是未定义的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-04
  • 2022-01-13
  • 1970-01-01
相关资源
最近更新 更多