【问题标题】:Conditionally specialize std::hash for std::shared_ptr struct有条件地将 std::hash 特化为 std::shared_ptr 结构
【发布时间】:2020-04-10 15:46:06
【问题描述】:

我有一个基类。目标是强制将 std::hash 用于 std::shared_ptr 与所有继承自 Base 的类。

我已经尝试了使用虚拟模板参数的以下方法,但编译器错误显然抱怨这是 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 类?

【问题讨论】:

  • AFAIR 您不能将全部或部分专业化添加到 std。编辑:原来,你can。 “程序定义类型的 std::hash 特化必须满足 Hash 要求。”

标签: c++ template-specialization enable-if stdhash


【解决方案1】:

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

std::hash 未在主模板中提供可用于 SFINAE 的第二个模板参数,但基于 this answer 您可以执行以下操作作为解决方法:

#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 的替代品。

【讨论】:

  • 我担心可能是这种情况。谢谢你的回答。
猜你喜欢
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 1970-01-01
  • 2014-06-13
  • 2014-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多