有条件地将 std::hash 特化为 std::shared_ptr 结构

Conditionally specialize std::hash for std::shared_ptr struct

我有一个基地class。目标是强制将 std::hash 专门化为 std::shared_ptr 以及从 Base 继承的所有 classes。

我尝试了以下使用虚拟模板参数的方法,但编译器错误显然抱怨这是 struct std::hash<std::shared_ptr<_Tp>>.

的重新定义
class Base {
};

class Derived : public Base {
};

namespace std {

template<typename T,
         typename std::enable_if<std::is_base_of<Base, T>::value, Base>::type* = nullptr
>
struct hash<std::shared_ptr<T>> {    
    size_t operator()(const std::shared_ptr<T>& d) const {
        return 616;
    }
};
}

我的问题是:是否可以像这样有条件地专门化 std class?

您问题中的代码的问题不是它是重定义,而是偏特化中不允许使用默认模板参数,并且偏特化中的所有模板参数都必须是可推导的。

std::hash 没有在主模板中提供可用于 SFINAE 的第二个模板参数,但基于 你可以做这样的事情作为解决方法:

#include <memory>
#include <utility>

class Base {};

class Derived : public Base {};

template <typename First, typename... Others>
using first = First;

namespace std {

template <typename T>
struct hash<first<std::shared_ptr<T>,
                  std::enable_if_t<std::is_base_of_v<Base, T>>>> {
  size_t operator()(const std::shared_ptr<T>& d) const { return 616; }
};

}  // namespace std

我认为原则上是可以的,因为声明取决于用户定义的类型 Base

关于此专业化是否应被视为 std::shared_ptr 标准专业化的重新定义,标准中存在未解决的问题。 (GCC 认为不是,Clang 认为是。)

但更重要的是,您仍然会遇到此部分专业化并不比标准为 std::shared_ptr 提供的部分专业化更专业的问题。因此,任何实际使用都会导致歧义错误,我认为没有任何方法可以使专业化更加专业化。

因此我认为您唯一的解决方案是为每个派生类型定义 std::hash 的显式特化,也许借助于宏。或者(可能更合适)您应该编写自己的哈希仿函数,并在需要时提供它作为 std::hash 的替代方法。