为什么在这种特殊情况下不需要将 std::hash() 的特化注入到 std 命名空间中?

Why doesn't specialization of std::hash() need to be injected into std namespace in this particular case?

考虑

using namespace std;

template <typename S, typename T> struct hash<pair<S, T>>
{
    inline size_t operator()(const pair<S, T> &v) const
    {
        return 0;
    }
};

在这种情况下,GCC 和 Clang 都可以正常编译,没有任何警告。然而,这似乎与我在网上读到的内容相矛盾,即定义您自己的哈希函数以与标准库的无序类型一起使用需要您将定义放在 std 命名空间中。

有趣的是,专门针对 pair<int, int>

template <> struct hash<pair<int, int>>
{
    size_t operator()(const pair<int, int> &v) const
    {
        size_t seed = 0;
        return seed;
    }
};

如我们所料导致错误。

但是,为什么第一个没有导致任何编译器警告,尽管我们没有将它放在 std 命名空间中?

如这里所写http://umich.edu/~eecs381/handouts/NamespaceGuide.pdf

"You are directing the compiler to make all of the names in the std namespace part of the global namespace, and thus they can be referred to without qualification in the rest of your source code file. They will now collide with any names that your own code uses. This takes effect only for the current compilation unit (the file that is being compiled)."

第一个是很好的例子,因为您将所有 std 内容(如 hash 模板)导入到您的全局名称空间,因此您可以毫无错误地编写它的专业化。在第二种情况下它不起作用,因为要专门化的任何 hash 模板泄漏。

这与Core Language Issue 727相关(删除文字是解析后删除,斜体文字是解析后新加,粗体文字是我强调的):

An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id or class-head-name is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (10.3.1 [namespace.def]), any namespace from its enclosing namespace set. Such a declaration may also be a definition may be declared in any scope in which the corresponding primary template may be defined (10.3.1.2 [namespace.memdef], 12.2 [class.mem], 17.6.2 [temp.mem]).

注意粗体文本,std::hash 的最近封闭命名空间是 std,并且您的显式特化未在 std 中声明,因此在解析之前它是格式错误的.偏特化没有这个约束,所以你的第一个例子在解析之前就已经是良构的了。

现在,您的两个示例在解析后都应该是格式正确的。您可以同时看到 Clang 和 MSVC accept the code (note an older version of Clang rejects it). For GCC, this is already a reported bug.