专门针对派生 类 的 std::hash 适用于 gcc,而不适用于 clang
Specializing std::hash for derived classes works in gcc, not clang
我正在尝试将 std::hash
专门化为派生的 classes。目前最好的方法是基于 :
#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>>>
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这是编译错误还是代码错误?
This question 提出了一个 SFINAE 解决方案,该解决方案在技术上适用于我的 gcc 和 clang 版本。然而,因为它只禁用了运算符,而不是 class,当人们试图散列任何不可散列的 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
我不想考虑宏解决方案。我进一步尝试了以下方法:
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>
在 class 模板偏特化中使用默认模板参数失败。
在 C++14 中是否有一种干净的模板元编程方法来实现这一点?
先吐槽一下:
std::hash 的设计很糟糕。不允许部分专业化。委员会应该简单地完整复制 boost 实现。
(咆哮)
我认为一个优雅的解决方案是从不同的角度来处理它:
#include <type_traits>
#include <functional>
#include <unordered_set>
namespace foo
{
template<class T, class E>
using first = T;
struct hashable {};
struct bar : public hashable {};
template<class T, typename = void>
struct hashable_hasher;
template<class T>
struct hashable_hasher<T, std::enable_if_t<std::is_base_of<hashable, T>::value>>
{
size_t operator()(const T& x) const { return 13; }
};
template<class T, typename = void>
struct choose_hash {
using type = std::hash<T>;
};
template<class T>
struct choose_hash<T, std::enable_if_t<std::is_base_of<hashable, T>::value>> {
using type = hashable_hasher<T>;
};
template<class T>
using choose_hash_t = typename choose_hash<T>::type;
template<class T>
using choose_set_t = std::unordered_set<T, choose_hash_t<T>>;
}
int main() {
foo::choose_set_t<foo::bar> baz;
return 0;
}
我正在尝试将 std::hash
专门化为派生的 classes。目前最好的方法是基于
#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>>>
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这是编译错误还是代码错误?
This question 提出了一个 SFINAE 解决方案,该解决方案在技术上适用于我的 gcc 和 clang 版本。然而,因为它只禁用了运算符,而不是 class,当人们试图散列任何不可散列的 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
我不想考虑宏解决方案。我进一步尝试了以下方法:
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>
在 class 模板偏特化中使用默认模板参数失败。
在 C++14 中是否有一种干净的模板元编程方法来实现这一点?
先吐槽一下:
std::hash 的设计很糟糕。不允许部分专业化。委员会应该简单地完整复制 boost 实现。
(咆哮)
我认为一个优雅的解决方案是从不同的角度来处理它:
#include <type_traits>
#include <functional>
#include <unordered_set>
namespace foo
{
template<class T, class E>
using first = T;
struct hashable {};
struct bar : public hashable {};
template<class T, typename = void>
struct hashable_hasher;
template<class T>
struct hashable_hasher<T, std::enable_if_t<std::is_base_of<hashable, T>::value>>
{
size_t operator()(const T& x) const { return 13; }
};
template<class T, typename = void>
struct choose_hash {
using type = std::hash<T>;
};
template<class T>
struct choose_hash<T, std::enable_if_t<std::is_base_of<hashable, T>::value>> {
using type = hashable_hasher<T>;
};
template<class T>
using choose_hash_t = typename choose_hash<T>::type;
template<class T>
using choose_set_t = std::unordered_set<T, choose_hash_t<T>>;
}
int main() {
foo::choose_set_t<foo::bar> baz;
return 0;
}