I am new to allocators. I am experimenting with a custom allocator used to allocate std::unordered_map
and string:
#include <string>
#include <string_view>
#include <unordered_map>
template < typename T >
struct MyMem
{
typedef T value_type;
T * allocate (std::size_t size)
{
return new T [size];
}
void deallocate ( T * t, std::size_t size ) noexcept
{
delete [] t;
}
};
using lstr = std::basic_string<
char,
std::char_traits<char>,
MyMem<char>
>;
struct lstrHash
{
std::size_t operator()(const lstr & x) const
{
std::string_view sv(&x[0], x.size());
return std::hash<decltype(sv)>()(sv);
}
};
struct lstrEqual
{
bool operator()(const lstr & x, const lstr & y) const
{
if (x.size() != y.size()) return false;
for (auto xi = x.begin(), yi = y.begin(); xi != x.end(); ++xi, ++yi)
{
if ( *xi != *yi ) return false;
}
return true;
}
};
template <typename Key, typename T,
typename Hash = std::hash<Key>,
typename KeyEqual = std::equal_to<Key> >
using unmap = std::unordered_map <
Key, T,
Hash,
KeyEqual,
MyMem<std::pair<const Key, T> > >;
int main()
{
unmap<lstr, int, lstrHash, lstrEqual> a;
unmap<lstr, int, lstrHash, lstrEqual> b(10);
}
However, the compiler produces the following error:
g++ -std=gnu++20 tests/soask.cpp -o soask
In file included from /home/i56087/opt/gcc-13/include/c++/13.2.1/bits/unordered_map.h:33,
from /home/i56087/opt/gcc-13/include/c++/13.2.1/unordered_map:41,
from tests/soask.cpp:3:
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h: In instantiation of ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_Hashtable(const _Hash&, const _Equal&, const allocator_type&) [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Value = std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; _ExtractKey = std::__detail::_Select1st; _Equal = lstrEqual; _Hash = lstrHash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; allocator_type = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >]’:
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h:1193:32: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_Hashtable(size_type, const _Hash&, const _Equal&, const allocator_type&) [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Value = std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; _ExtractKey = std::__detail::_Select1st; _Equal = lstrEqual; _Hash = lstrHash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; size_type = long unsigned int; allocator_type = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >]’
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/unordered_map.h:162:9: required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(size_type, const hasher&, const key_equal&, const allocator_type&) [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Tp = int; _Hash = lstrHash; _Pred = lstrEqual; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; size_type = long unsigned int; hasher = lstrHash; key_equal = lstrEqual; allocator_type = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >]’
tests/soask.cpp:66:45: required from here
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h:489:27: error: too many initializers for ‘std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>, MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >, std::__detail::_Select1st, lstrEqual, lstrHash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::__node_alloc_type’
489 | __hashtable_alloc(__node_alloc_type(__a)),
| ^~~~~~~~~~~~~~~~~~~~~~
In file included from /home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h:35:
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable_policy.h: In instantiation of ‘void std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_deallocate_buckets(__buckets_ptr, std::size_t) [with _NodeAlloc = MyMem<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>, true> >; __buckets_ptr = std::__detail::_Hashtable_alloc<MyMem<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>, true> > >::__node_base**; std::size_t = long unsigned int]’:
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h:451:42: required from ‘void std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_M_deallocate_buckets(__buckets_ptr, size_type) [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Value = std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; _ExtractKey = std::__detail::_Select1st; _Equal = lstrEqual; _Hash = lstrHash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; __buckets_ptr = std::__detail::_Hashtable_alloc<MyMem<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>, true> > >::__node_base**; size_type = long unsigned int]’
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h:456:30: required from ‘void std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_M_deallocate_buckets() [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Value = std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; _ExtractKey = std::__detail::_Select1st; _Equal = lstrEqual; _Hash = lstrHash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>]’
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h:1609:28: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::~_Hashtable() [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Value = std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; _ExtractKey = std::__detail::_Select1st; _Equal = lstrEqual; _Hash = lstrHash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>]’
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/unordered_map.h:148:7: required from here
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable_policy.h:2058:28: error: too many initializers for ‘MyMem<std::__detail::_Hash_node_base*>’
2058 | __buckets_alloc_type __alloc(_M_node_allocator());
| ^~~~~~~
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable_policy.h: In instantiation of ‘std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_base** std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_buckets(std::size_t) [with _NodeAlloc = MyMem<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>, true> >; __buckets_ptr = std::__detail::_Hashtable_alloc<MyMem<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>, true> > >::__node_base**; std::size_t = long unsigned int]’:
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h:442:47: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::__buckets_ptr std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_M_allocate_buckets(size_type) [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Value = std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; _ExtractKey = std::__detail::_Select1st; _Equal = lstrEqual; _Hash = lstrHash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; __buckets_ptr = std::__detail::_Hashtable_alloc<MyMem<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>, true> > >::__node_base**; size_type = long unsigned int]’
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable.h:1198:17: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::_Hashtable(size_type, const _Hash&, const _Equal&, const allocator_type&) [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Value = std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int>; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; _ExtractKey = std::__detail::_Select1st; _Equal = lstrEqual; _Hash = lstrHash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; size_type = long unsigned int; allocator_type = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >]’
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/unordered_map.h:162:9: required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(size_type, const hasher&, const key_equal&, const allocator_type&) [with _Key = std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >; _Tp = int; _Hash = lstrHash; _Pred = lstrEqual; _Alloc = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >; size_type = long unsigned int; hasher = lstrHash; key_equal = lstrEqual; allocator_type = MyMem<std::pair<const std::__cxx11::basic_string<char, std::char_traits<char>, MyMem<char> >, int> >]’
tests/soask.cpp:66:45: required from here
/home/i56087/opt/gcc-13/include/c++/13.2.1/bits/hashtable_policy.h:2042:28: error: too many initializers for ‘MyMem<std::__detail::_Hash_node_base*>’
2042 | __buckets_alloc_type __alloc(_M_node_allocator());
| ^~~~~~~
It seems my allocator has some missing definitions, but the compiler error message is too hard for me to decipher. ChatGPT also says the error may be due to the fact that the allocator is incompatible with the stl container. How to correctly structure my allocator to make it work?
Thanks!
The Allocator requirements indicate that an allocator for type T
must be copy constructible from a corresponding allocator of different type U
.
X u(b);
Postconditions: Y(u) == b and u == X(b).
where
It is necessary because the internal allocator, which is the allocator actually used by the container to perform allocate and deallocate operations, may be specialized for a different type than the allocator_type
.
In this case, the std::unordered_map
container requires to store key-value pairs into node types and to instantiate an array of pointers as a bucket array. This means that the allocator instance must be converted to two different allocator specializations and vice versa to achieve various works. The existence of such a heterogeneous copy constructor makes it possible.