派生类的std :: hash专门用于gcc,而不是clang [英] Specializing std::hash for derived classes works in gcc, not clang

查看:169
本文介绍了派生类的std :: hash专门用于gcc,而不是clang的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图为derved类专门化 std :: hash 。到目前为止,最好的方法是基于这个答案

  #include< type_traits> 
#include< functional>
#include< unordered_set>

namespace foo
{
template< class T,class E>
使用first = T;

struct hashable {};
struct bar:public hashable {};
}

命名空间std
{
模板< 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>巴兹;
返回0;



$ b

这个编译时使用g ++ 5.2.0,没有警告( -Wall -pedantic ),但是使用clang ++ 3.7.0会导致以下错误:

  first.cpp:17:12:error:类模板部分特化不专用任何模板参数;定义主模板,删除模板参数列表
struct hash< foo :: first< T,std :: enable_if_t< std :: is_base_of< foo :: hashable,T> :: value>>>
^〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这是编译器错误还是代码中的错误?

这个问题,提出了一个SFINAE解决方案,它在技术上适用于我的gcc和clang版本。但是,因为它只禁用了运算符,而不是类,所以当试图散列任何不可哈希类时,它开始产生非常混乱的错误消息:

 模板< 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>湾;
...
type_traits:2388:44:error:在'std :: enable_if< false,unsigned long>'中没有类型命名'type';
'enable_if'不能用于禁用此声明

我不想考虑宏解决方案。我进一步尝试了以下方法:

  template< typename T> 
struct hash< std :: enable_if_t< std :: is_base_of< foo :: hashable,T> :: value,T>>

两位编译器都抱怨说他们无法推断出这种类型,我觉得这很刺激,因为我没有看到对于第一个解决方案来说,差异很大。



我的第一次尝试是通常的 enable_if

 模板< typename T,
typename DUMMY = std: :enable_if_t< std :: is_base_of< foo :: hashable,T> :: value>>
struct hash< T>

在类模板部分特化中,默认模板参数失败。



是否有一种干净的模板元编程方式可以在C ++ 14中实现这一点?

首先有点咆哮:

std :: hash的设计很糟糕。部分专业化是不允许的。委员会应该完全复制boost实现。



(咆哮)



我认为一个优雅的解决方案是从另一个角度来处理它:

  #include< type_traits> 
#include< functional>
#include< unordered_set>

namespace foo
{
template< class T,class E>
使用first = T;

struct hashable {};
struct bar:public hashable {};

模板< class T,typename = void>
struct hashable_hasher;

模板< 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; }
};


模板< class T,typename = void>
struct choose_hash {
using type = std :: hash< T>;
};

模板< class T>
struct choose_hash< T,std :: enable_if_t< std :: is_base_of< hashable,T> :: value>> {
使用type = hashable_hasher< T>;
};

模板< class T>
using choose_hash_t = typename choose_hash< T> :: type;

模板< class T>
using choose_set_t = std :: unordered_set< T,choose_hash_t< T>>;
}

int main(){
foo :: choose_set_t< foo :: bar>巴兹;
返回0;
}


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?

解决方案

first a little rant:

the design of std::hash is awful. Partial specialisations are not allowed. the committee should have simply copied the boost implementation in full.

(rant over)

I think one elegant solution is to approach it from a different angle:

#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专门用于gcc,而不是clang的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆