【发布时间】:2016-01-20 07:56:37
【问题描述】:
我正在尝试将 std::hash 专门用于衍生课程。迄今为止最好的方法是基于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;
}
使用 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 版本。但是,因为它只禁用了运算符,而不是类,所以当尝试对任何不可散列的类进行散列时,它开始产生非常混乱的错误消息:
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显然不是一个好主意。您可以创建一个hash_base<T>,然后为可散列类型添加template<> struct hash<your_type> : hash_base<your_type> {};,而不是从hashable派生您的类。 -
@BoPersson 我想避免为每种类型添加额外的行,尤其是因为它必须位于丑陋的命名空间中。
-
@Jarod42 这意味着,该标准甚至没有描述(当前)
first-solution 应该如何编译,对吧?编辑:clang 似乎对待它不同,而 gcc 坚持使用 SF 的建议解决方案。 -
@Zulan 我想这不是重点。您的代码确实触发了替换失败,但是(成功时)别名模板等同于它指向的类型,因此您最终会得到
hash<T>这不是专业化
标签: c++ gcc clang c++14 sfinae