【问题标题】:Partial std::hash specialization for const and non-const typesconst 和非 const 类型的部分 std::hash 特化
【发布时间】:2014-10-26 20:14:08
【问题描述】:

我在为我的一门课程执行 std::hash 的部分特化时遇到问题。这是一个独立的示例。

编辑:感谢 Piotr,我更改了代码以包含修复程序,但错误仍然存​​在。

#include <functional>
#include <string>
#include <unordered_set>
#include <unordered_map>

class Base
{
public:
  template <class T>
  using const_pset = std::unordered_set<const T *, std::hash <T *>, std::equal_to <T *> >;
  template <class T, class U>
  using const_pmap = std::unordered_map<const T *, U, std::hash <T *>, std::equal_to <T *> >;
};

class A : public Base
{
public:
  std::size_t hash () const
  {
    std::hash<std::string> hashfn;
    return hashfn (str);
  }
  bool operator== (const A &a) const { return str == a.str; }
  std::string str;
};

namespace std
{
    template <>
    struct hash <A *>
    {
      typedef A argument_type;
      typedef std::size_t result_type;

      result_type operator () (const argument_type *op) const
      {
    return op->hash ();
      }
    };
    template <>
    struct hash <const A *>
    {
      typedef A argument_type;
      typedef std::size_t result_type;

      result_type operator () (const argument_type *op) const
      {
    return op->hash ();
      }
    };
};

extern const A* get_a ();

typedef Base::const_pmap<A, Base::const_pset<A> > mapA;

int
work (mapA &mapa)
{
  const A *a = get_a ();
  mapa[a];
  return 0;
}

如果你用 (g++ 4.9.0) 编译它:

$ g++ -std=gnu++11 -Wall -c test.cc 2>&1 | head -n 50
In file included from /tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable.h:35:0,
                 from /tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/unordered_set:47,
                 from test.cc:3:
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable_policy.h: In instantiation of 'static bool std::__detail::_Equal_helper<_Key, _Value, _ExtractKey, _Equal, _HashCodeType, true>::_S_equals(const _Equal&, const _ExtractKey&, const _Key&, _HashCodeType, std::__detail::_Hash_node<_Value, true>*) [with _Key = const A*; _Value = std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<A*>; _HashCodeType = long unsigned int]':
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable_policy.h:1707:23:   required from 'bool std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::_M_equals(const _Key&, std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::__hash_code, std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::__node_type*) const [with _Key = const A*; _Value = std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<A*>; _H1 = std::hash<A*>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _Traits = std::__detail::_Hashtable_traits<true, false, true>; std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::__hash_code = long unsigned int; std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::__node_type = std::__detail::_Hash_node<std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > >, true>]'
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable.h:1391:4:   required from 'std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__node_base* std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_M_find_before_node(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type, const key_type&, std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__hash_code) const [with _Key = const A*; _Value = std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > >; _Alloc = std::allocator<std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > > >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<A*>; _H1 = std::hash<A*>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__node_base = std::__detail::_Hash_node_base; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type = long unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::key_type = const A*; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__hash_code = long unsigned int]'
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable.h:590:65:   required from 'std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__node_type* std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_M_find_node(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type, const key_type&, std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__hash_code) const [with _Key = const A*; _Value = std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > >; _Alloc = std::allocator<std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > > >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<A*>; _H1 = std::hash<A*>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__node_type = std::__detail::_Hash_node<std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > >, true>; typename _Traits::__hash_cached = std::integral_constant<bool, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type = long unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::key_type = const A*; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__hash_code = long unsigned int]'
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable_policy.h:597:60:   required from 'std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](const key_type&) [with _Key = const A*; _Pair = std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > >; _Alloc = std::allocator<std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > > >; _Equal = std::equal_to<A*>; _H1 = std::hash<A*>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> >; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = const A*]'
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/unordered_map.h:627:20:   required from 'std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](const key_type&) [with _Key = const A*; _Tp = std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> >; _Hash = std::hash<A*>; _Pred = std::equal_to<A*>; _Alloc = std::allocator<std::pair<const A* const, std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> > > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::unordered_set<const A*, std::hash<A*>, std::equal_to<A*>, std::allocator<const A*> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = const A*]'
test.cc:61:9:   required from here
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable_policy.h:1326:74: error: invalid conversion from 'const A*' to 'A*' [-fpermissive]
     { return __c == __n->_M_hash_code && __eq(__k, __extract(__n->_M_v())); }
                                                                          ^
In file included from /tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/functional:49:0,
                 from test.cc:1:
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/stl_function.h:339:7: note: initializing argument 1 of 'bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = A*]'
       operator()(const _Tp& __x, const _Tp& __y) const
       ^
In file included from /tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable.h:35:0,
                 from /tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/unordered_set:47,
                 from test.cc:3:
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/hashtable_policy.h:1326:74: error: invalid conversion from 'type {aka const A*}' to 'A*' [-fpermissive]
     { return __c == __n->_M_hash_code && __eq(__k, __extract(__n->_M_v())); }
                                                                          ^
In file included from /tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/functional:49:0,
                 from test.cc:1:
/tools/oss/packages/x86_64-rhel5/gcc/4.9.0/include/c++/4.9.0/bits/stl_function.h:339:7: note: initializing argument 2 of 'bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = A*]'
       operator()(const _Tp& __x, const _Tp& __y) const
       ^

我不明白为什么会收到:error: invalid conversion from 'const A*' to 'A*' [-fpermissive]。 关于改进代码的任何建议?鉴于模板正文相同,必须为 Aconst A 专门化哈希似乎很奇怪。

【问题讨论】:

  • 开始于:bool operator== (const A &amp;a) {} -> bool operator== (const A &amp;a) const {}
  • 另外,您将hash 专门用于Aconst Ahash&lt;A&gt;hash&lt;const A&gt;),但最终您将hash&lt;A*&gt; 用于存储const A*

标签: c++ c++11 hash stdhash


【解决方案1】:

抱歉,刚刚注意到我还需要将 const 添加到 std::hash 和 std::equal_to 参数:

template <class T, class U>
  using const_pmap = std::unordered_map<const T *, U, std::hash <const T *>, std::equal_to <const T *> >

我仍然很高兴收到有关改进代码的答案。

【讨论】:

    【解决方案2】:

    您为A 编写了您的专业化,但您将它与A* 一起使用,所以您需要:

    namespace std
    {
        template <>
        struct hash<A*>
        {
          typedef const A* argument_type;
          typedef std::size_t result_type;
    
          result_type operator () (const argument_type op) const
          {
                return op->hash ();
          }
        };   
    }
    

    你的std::unordered_mapconst A*

    template <class T, class U>
    using const_pmap = std::unordered_map<const T *, U, std::hash <T *>, std::equal_to <const T *> >;
    

    但是由于equal_to默认调用operator==,你实际上并不需要它(除非你也想专门化它):

    class Base
    {
    public:
      template <class T>
      using const_pset = std::unordered_set<const T *, std::hash <T *> >;
      template <class T, class U>
      using const_pmap = std::unordered_map<const T *, U, std::hash <T *>>;
    };
    

    Compiled Demo

    【讨论】:

    • 正如我之前提到的,您还需要std::hash &lt;const T *&gt;。谢谢。
    • @PauloJ.Matos : 不,你不知道 : ) 请参阅 here 获取工作示例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-14
    相关资源
    最近更新 更多