Частичная специализация std :: hash для константных и неконстантных типов

У меня проблемы с некоторым кодом, выполняющим частичную специализацию std::hash для одного из моих классов. Вот отдельный пример.

РЕДАКТИРОВАТЬ: Спасибо Петр, я изменил код, чтобы включить исправления, но ошибка остается.

#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],
Есть предложения по улучшению кода? Кажется странным, что нужно специализировать хэш для обоих A а также const A с учетом тела шаблона то же самое.

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 *> >

Я все еще рад получить ответы об улучшении кода.

1

Другие решения

Вы написали свою специализацию для 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_map для const 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 *>>;
};

Демо-версия

1

По вопросам рекламы [email protected]