Suppose I have a custom stateful allocator that I want to use for a STL container. The container is going to use the rebind_alloc
trait to allocate its internal elements, thus by default (or by defining rebind<T>::other
), I can ask it to use my allocator class for the internals as well.
However, I would like to pass the state of my allocator.
This related question asks why
template<class U> allocator( const allocator<U> & o ) throw()
exists and the answer suggests that what I want to do is possible.
However, from my experiments (basically tracing calls to Allocator()
and template <class U> Allocator(const Allocator<U> &other)
in a custom allocator), I couldn't get the rebind_alloc
conversion constructor to be called instead of the default constructor.
It was actually quite simple, the conversion constructor will be called if the container's constructor was given an allocator instance as argument. It is logical that the container's constructor has to know the allocator instance.
For example, if the container is not given any argument, the default constructor of the allocator will be used.
#include <memory>
#include <set>
#include <iostream>
#include <type_traits>
void *default_allocator;
template <typename T>
struct Allocator
{
using value_type = T;
using pointer = T *;
using size_type = size_t;
int state;
Allocator(int s) : state(s)
{
std::cout << "default with arg " << typeid(T).name() << std::endl;
std::cout << "state " << state << std::endl;
}
Allocator() : Allocator(*static_cast<Allocator *>(default_allocator)) // state(static_cast<Allocator *>(default_allocator)->state)
{
std::cout << "default without arg " << typeid(T).name() << std::endl;
std::cout << "state " << state << std::endl;
}
template <class U>
Allocator(const Allocator<U> &other)
{
state = other.state;
std::cout
<< "conversion " << typeid(T).name() << " from " << typeid(U).name() << std::endl;
std::cout << "state " << state << std::endl;
}
pointer allocate(size_type n)
{
std::allocator<T> a;
return a.allocate(n);
}
void deallocate(pointer p, size_type n)
{
std::allocator<T> a;
a.deallocate(p, n);
}
};
template <class T, class Compare = std::less<T>>
using set = std::set<T, Compare, Allocator<T>>;
int main()
{
std::cout << "new Allocator<int>(10)" << std::endl;
default_allocator = static_cast<void *>(new Allocator<int>(10));
std::cout << std::endl;
std::cout << "alloc(20)" << std::endl;
Allocator<int> alloc(20);
std::cout << std::endl;
std::cout << "set<int> s1;" << std::endl;
set<int> s1;
std::cout << std::endl;
std::cout << "set<int> s2(alloc);" << std::endl;
set<int> s2(alloc);
}
new Allocator<int>(10)
default with arg i
state 10
alloc(20)
default with arg i
state 20
set<int> s1;
default without arg NSt3__111__tree_nodeIiPvEE
state 10
set<int> s2(alloc);
conversion NSt3__111__tree_nodeIiPvEE from i
state 20