Я пытаюсь специализировать std::hash
для производных классов. Пока лучший подход основан на этом ответе:
#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>>>
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Это ошибка компилятора или ошибка в коде?
Этот вопрос предлагает решение 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?
template<class T, class E> first = T;
действительно преувеличивает, и специализироваться наE
, очевидно, не лучшая идея. Вместо того, чтобы наследовать классы отhashable
, вы можете создатьhash_base<T>
, а затем добавитьtemplate<> struct hash<your_type> : hash_base<your_type> {};
для хэшируемых типов. - person Bo Persson   schedule 21.10.2015first
-решение, верно? Редактировать: И clang, похоже, относится к этому по-разному, в то время как gcc придерживается предложенного решения по использованию SF. - person Zulan   schedule 21.10.2015hash<T>
, который не является специализацией. - person Piotr Skotnicki   schedule 21.10.2015