我正在努力专攻std::hash
对于派生类。迄今为止最好的方法是基于这个答案 https://stackoverflow.com/a/31213703/620382:
#include <type_traits>
#include <functional>
#include <unordered_set>
namespace foo
{
template<class T, class E>
using first = T;
struct hashable {};
struct bar : public hashable {};
}
namespace std
{
template <typename T>
struct hash<foo::first<T, std::enable_if_t<std::is_base_of<foo::hashable, T>::value>>>
{
size_t operator()(const T& x) const { return 13; }
};
}
int main() {
std::unordered_set<foo::bar> baz;
return 0;
}
使用 g++ 5.2.0 进行编译,没有警告(-Wall -pedantic
),但使用 clang++ 3.7.0 会导致以下错误:
first.cpp:17:12: error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list
struct hash<foo::first<T, std::enable_if_t<std::is_base_of<foo::hashable, T>::value>>>
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这是编译器错误还是代码错误?
这个问题 https://stackoverflow.com/q/21900707/620382,提出了一个 SFINAE 解决方案,在技术上可以与我的 gcc 和 clang 版本一起使用。然而,因为它只禁用了运算符,而不是类,所以当尝试对任何不可散列的类进行散列时,它会开始产生非常混乱的错误消息:
template <typename T>
struct hash
{
typename std::enable_if_t<std::is_base_of<foo::hashable, T>::value, std::size_t>
operator()(const T& x) const { return 13; }
};
...
struct fail {};
std::unordered_set<fail> bay;
...
type_traits:2388:44: error: no type named 'type' in 'std::enable_if<false, unsigned long>';
'enable_if' cannot be used to disable this declaration
我不想考虑宏观解决方案。我进一步尝试了以下方法:
template <typename T>
struct hash<std::enable_if_t<std::is_base_of<foo::hashable, T>::value, T>>
两个编译器都抱怨它们无法推断出类型,我觉得这很令人恼火,因为我没有看到与first
解决方案。
我的第一次尝试是通常的常见模式enable_if
:
template <typename T,
typename DUMMY = std::enable_if_t<std::is_base_of<foo::hashable, T>::value>>
struct hash<T>
类模板部分特化中的默认模板参数会失败。
在 C++14 中是否有一种干净的模板元编程方法可以实现此目的?