In my C++11 program, I use shared_ptr
for some objects which are actively created and deleted. It so happened that standard allocator with operat
Your custom allocator does not meet the C++ Allocator requirements.
In particular, it does not support being rebound to allocate objects of a different type. Usually allocators are templates, parameterized on the type they allocate memory for. allocate_shared
needs to rebind the allocator so it can allocate a block of memory of the appropriate size and type, it does not want to allocate an array of char objects.
// MyAlloc is a correct allocator, since allocator_traits can be instantiated
This is not a correct assumption. Instantiating allocator_traits<MyAlloc>
does not instantiate all its members.
Also, is it OK to use char as value_type for this particular allocator
That makes your allocator an allocator of char
, but allocate_shared
needs an allocator of some_internal_type_defined_by_the_library
and so it tries to use std::allocator_traits<MyAlloc>::rebind_alloc<some_internal_type_defined_by_the_library>
to get an allocator for that type, but your allocator does not support the rebind requirement.
If your allocator is a template of the form MyAlloc<T>
then allocator_traits
can determine how to rebind it to MyAlloc<U>
, otherwise the type MyAlloc::rebind<U>::other
needs to be valid.
The C++ standard shows the following as an example of an allocator supporting the minimum requirements for a C++ Allocator type:
template <class Tp>
struct SimpleAllocator {
typedef Tp value_type;
SimpleAllocator(ctor args);
template <class T> SimpleAllocator(const SimpleAllocator<T>& other);
Tp* allocate(std::size_t n);
void deallocate(Tp* p, std::size_t n);
};
template <class T, class U>
bool operator==(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
template <class T, class U>
bool operator!=(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
Like this.. You need it templated, you need the rebind and the types and the allocate and deallocate members. It is also nice to have the operators..
#include <memory>
template<typename T>
struct Allocator
{
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
template<typename U>
struct rebind {typedef Allocator<U> other;};
Allocator() throw() {};
Allocator(const Allocator& other) throw() {};
template<typename U>
Allocator(const Allocator<U>& other) throw() {};
template<typename U>
Allocator& operator = (const Allocator<U>& other) { return *this; }
Allocator<T>& operator = (const Allocator& other) { return *this; }
~Allocator() {}
pointer allocate(size_type n, const void* hint = 0)
{
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* ptr, size_type n)
{
::operator delete(ptr);
}
};
template <typename T, typename U>
inline bool operator == (const Allocator<T>&, const Allocator<U>&)
{
return true;
}
template <typename T, typename U>
inline bool operator != (const Allocator<T>& a, const Allocator<U>& b)
{
return !(a == b);
}
int main()
{
std::allocate_shared<int, Allocator<int>>(Allocator<int>(), 0);
}
At the very LEAST, an allocator could look like:
template<typename T>
struct Allocator
{
typedef T value_type;
Allocator() noexcept {};
template<typename U>
Allocator(const Allocator<U>& other) throw() {};
T* allocate(std::size_t n, const void* hint = 0)
{
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* ptr, size_type n)
{
::operator delete(ptr);
}
};
template <typename T, typename U>
inline bool operator == (const Allocator<T>&, const Allocator<U>&)
{
return true;
}
template <typename T, typename U>
inline bool operator != (const Allocator<T>& a, const Allocator<U>& b)
{
return !(a == b);
}
This will also work for allocate_shared
.. However, being the type of person I am, I prefer to have all the functions.. Even the ones not required/used by said container/function.