The problem is much harder than it looks. Because the allocator type is part of the type of the object there is little interaction allowed between types that differ only in allocator. The first example that comes to mind is that a function that takes a std::string
by constant reference cannot take a string that uses a different allocator. One particular such case is during construction of objects. As a matter of fact it is probably the harder case here.
Consider for example the following code:
// Assume that you could construct from a different allocator
std::vector<int, allocator1> f() {
std::vector<int, allocator2> r;
// fill in
return r;
}
int main() {
std::vector<int, allocator3> x = f();
}
Consider that allocator1
is std::allocator<int>
(i.e. the default allocator), that allocator2
uses a local arena in the stack, and that allocator3
might use shared memory. In theory the code is quite simple, the vector r
is created and filled with data, on the return statement a new temporary is created by copying from r
, and finally x
is constructed by copying from that temporary. The problem is that the standard allows (and compilers like) to avoid copying as much as possible. In the particular example above (and ignoring allocators) the compiler would elide both copies and create the buffer only once, which is fast and efficient. But with allocators potentially being different, NRVO and other types of copy-elision would have to be disabled. (If those optimizations are enabled, x
in main would be using allocator2
, with a local arena that has already been destroyed, causing undefined behavior).
By enabling copy construction from containers with one allocator into another you might end up in a mess, or in a deeper mess than we are already in the current standard, where you can cause all sort of interesting issues with stateful allocators (say that you use per-thread allocators, and that you move data into a shared queue, you might end up with one thread holding an object created by the per-thread allocator on another thread, and because the point of using per-thread allocators is avoid contentions on locks, you could be creating a race condition on apparently safe code....
This is an old proposal to the C++ committee Towards a better allocation model that raises some of the concerns with the C++03 allocation model and proposes a polymorphic allocator type (which has problems of it's own). It makes for an interesting read, but beware of the details, not everything is as good as it seems, and there are quite a few pitfalls in using either option (or the C++11 version that is similar to the C++03 version)