11

从 C++11 标准中我不清楚hash<T>应该在哪里定义用户定义的函子。

例如,在23.5.2 Header<unordered_map>中,它显示:

template <class Key,
        class T,
        class Hash = hash<Key>,
        class Pred = std::equal_to<Key>,
        class Alloc = std::allocator<std::pair<const Key, T> > >
    class unordered_map;

这表明,默认情况下,hash<T>在全局命名空间equal_to<>中搜索,而在std命名空间中搜索。

hash<>为什么和之间的命名空间有区别equal_to<>

(实际上,在http://www.cplusplus.com/reference/unordered_map/unordered_map/的描述中,都没有指定std命名空间。)

因此,当hash<>为用户类型定义函子时,我们应该将它包含在一个namespace std { }块中,还是可以保留在当前命名空间中?

如果代码没有using namespace std;,那么 STL 容器如何unordered_map知道在std命名空间中查找hash<>与原始类型关联的预定义函子?似乎默认值Hash = hash<Key>无法找到这些。

对不起,如果这些是愚蠢的问题..

4

2 回答 2

6

首先,模板没有“参数依赖查找”。因此hash<Key>将始终引用相同的模板,无论是在全局命名空间中std还是在全局命名空间中,独立于Key. 如果它已解析为不同翻译单元中的不同模板,则会因 ODR 违规而导致未定义的行为。仅此一项就表明,hash这里的意思std::hash是,就像unordered_map是这样声明的:

namespace std {
    template<class T> struct hash;

    template <class Key,
        class T,
        class Hash = hash<Key>,    // resolves to std::hash<Key> for all Keys
        class Pred = std::equal_to<Key>,
        class Alloc = std::allocator<std::pair<const Key, T> > >
    class unordered_map;
}

但是,标准头文件中声明的类型不需要在源代码中编写(原则上它们可以内置于编译器或通过其他魔法预编译)。该标准要求每个标准标头仅声明其概要中的类型,这意味着通过省略std::hash声明,该标准允许一些假设的实现以避免上述命名空间污染。这解释了为什么您在概要中看不到上述声明。

为了进一步支持上述结论,我们转到 §20.8.12 类模板哈希 [unord.hash],内容如下:

23.5 中定义的无序关联容器使用类模板散列的特化作为默认散列函数。

本段指的是std::hash,我们可以从 的概要中推断出来<functional>

底线:这是标准格式的不一致。有很多不一致的地方,所以这个具体案例一点也不奇怪。在这种情况下,人们必须通过推断什么是唯一明智的事情来理解其意图。

专业化。您在声明它们的名称空间中专门化模板。您被明确授予为您自己的类型专门化标准模板的权利:

namespace std {
    template<> struct hash<YourClass> {
        // specialization goes here
    };
}
于 2013-02-03T07:31:23.367 回答
-1

当您想为您的类型定义一个哈希函子时,只需将其放入您的命名空间并用它进行实例化unordered_xxx——非常简单......

namespace my {
struct some_type {/*...*/};
struct some_hasher {/*...*/};
}

typedef std::unordered_map<int, my::some_type, my::some_hasher> my_some_hash_map;

AFAIK 不建议(而且显然不安全)将 smth 添加到std命名空间中。实际上它不是必需的(根据我的经验,我不记得什么时候想添加 smth std——总是可以解决不这样做的“问题”)

顺便说一句:

这表明,默认情况下,在全局命名空间中搜索 hash,而在 std 命名空间中搜索 equal_to<>。

错误的!考虑一下:

namespace my {
// See declaration of some_type above...
template <
    typename SomeType = some_type
  , typename Alloc = std::allocator<SomeType>
  >
struct test;
}

显然,some_type将在当前命名空间中搜索!

因此,当为用户类型定义 hash<> 仿函数时,我们应该将它包含在一个namespace std { }块中,还是可以保留在当前命名空间中?

不!不要试图通过提供模板特化来减少类型的符号数std::hash<YourType>——只需在命名空间中编写自己的哈希函子并在实例化时将其添加为模板参数std::unordered_xxx...

遵循这个简单的规则:避免将 smth 添加到namespace不受您控制的 a 中(所有名称空间!不仅std......)除非是(某些库)明确要求的情况。

于 2013-02-03T07:31:33.780 回答