I am trying to specialize std::hash
for derved classes. The best approach so far is based on this answer:
#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;
}
This compiles with g++ 5.2.0 with no warnings (-Wall -pedantic
), but with clang++ 3.7.0 it results in the following error:
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>>>
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Is this a compiler error or an error in the code?
This question, proposes a SFINAE solution that technically works with both my gcc and clang versions. However, because it only disabled the operator, not the class, It starts to yield very confusing error messages when one tries to hash any non-hashable class:
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
I would like to not consider the macro solution. I have further tried the following approaches:
template <typename T>
struct hash<std::enable_if_t<std::is_base_of<foo::hashable, T>::value, T>>
Both compiler complain that they cannot deduce the type, which I find rather irritating because I don't see much of a difference towards the first
solution.
My first attempt was the usual common pattern for enable_if
:
template <typename T,
typename DUMMY = std::enable_if_t<std::is_base_of<foo::hashable, T>::value>>
struct hash<T>
Which fails with default template argument in a class template partial specialization.
Is there a clean template metaprogramming way to acchieve this in C++14?