When trying to invoke a custom templated PoolAllocator<T> for std::unordered_map<T>, I get segfaults when accessing the map with the operator[]. Using this allocator with std::map, std::vector and also a placed new() (as functor with overloaded operator to allocate memory for single elements of type T) is stable.
To use the allocator for my planned purpose for std::map and std::unordered_map, I typedef it as:
typedef PoolAllocator<std::pair<unsigned int, MyClass> > MapAllocator;
The constructor of MapAllocator for myMap reports a sizeof(T) of unexpected 60 bytes, and using myMap causes no crash.
The constructor of MapAllocator for myUnorderedMap reports a sizeof(T) of 44 bytes, as expected, but using myUnorderedMap crashes. But If I "cheat" the MapAllocator for myUnorderedMap to be typedef PoolAllocator<char[60]> MapAllocator instead, the code compiles and myUnorderedMap also works without crashes!
Where do the extra 16 bytes come from?
There must be something fundamental I did not understand here, and I really appreciate the help!
I don't know how it accomplish this but it seems like the template type T is not actually MyPair as you specified in the typedef but instead some other type that contains a MyPair plus any extra data that the container needs.
Let us say we want to implement a doubly linked list containing values of type double.
a la std::list<double> where the allocator specified as the template argument is std::allocator<double>
Say, the link in out linked list is defined as:
1 2 3 4 5 6 7 8
struct link // node in a linked list of double
{
double value = 0 ;
link* next = nullptr ;
link* previous = nullptr ;
// ...
};
The list needs storage for objects of type Link;
however, the allocator type for its value_type is std::allocator<double>
The list, internally has to use std::allocator<link>
Ergo, the need for rebinding an allocator (The allocator must support rebind; this alows the container to get a compatible allocator which can allocate storage for the internal type that it needs). http://en.cppreference.com/w/cpp/memory/allocator_traits
#include <iostream>
#include <memory>
#include <type_traits>
int main() {
// std::allocator<double> allocates storage for objects of type double
static_assert( std::is_same_v< std::allocator<double>::value_type, double > ) ;
std::cout << sizeof(std::allocator<double>::value_type) << ' ' << sizeof(double) << '\n' ;
struct link // node in a linked list of double
{
double value = 0 ;
link* next = nullptr ;
link* previous = nullptr ;
// ...
};
// in a linked list of double, though the value_type of the list is double,
// the allocator used internally must allocate storage of objects of type link.
// this is achieved by rebinding the allocator (for the value_type double)
// rebinding the original allocator yields linked_list_allocator
// which can allocate storage for objects of type link
using linked_list_allocator = std::allocator_traits< std::allocator<double> >::rebind_alloc<link> ;
static_assert( std::is_same_v< linked_list_allocator::value_type, link > ) ;
std::cout << sizeof(linked_list_allocator::value_type) << ' ' << sizeof(link) << '\n' ;
}
If the custom allocator causes a crash, check if it meets the requirements of a standard library compatible allocator (in particular, check the rebind operation): http://en.cppreference.com/w/cpp/concept/Allocator
I have a rebind, which should explain why std::map works and using the allocator with it reports a sizeof(T) different to sizeof(std::pair<const unsigned, MyType>).