My question here is basically a follow up to :
How can I write a stateful allocator in C++11, given requirements on copy construction?
Basically, despite the fact that the C++11 standard now allows for stateful allocators, we still have the requirement that if you copy a certain Allocator
, the copy must compare equal via the ==
operator with the original. This indicates that the copy can safely deallocate memory which was allocated by the original, and vice-versa.
So, right off the bat this already prohibits an allocator from maintaining unique internal state, like a slab-allocator or memory pool or something. One solution would be to use a shared_ptr
pointer-to-implementation idiom for the internal state, so that all copies of some original Allocator
use the same underlying memory pool. That's not too bad. Except...
According to the above referenced question, as well as the accepted answer, the standard also seems to require that Allocator<T>
has an interoperable copy constructor with Allocator<U>
, so that:
Allocator<T> alloc1;
Allocator<U> alloc2(alloc1);
assert(alloc1 == alloc2); // must hold true
So in other words, allocator types must be interoperable regardless of different template parameters. That means if I allocate some memory using Allocator<T>
, I must be able to deallocate that memory using an Allocator<U>
instance constructed from the original Allocator<T>
.
...and that's pretty much a show-stopper for any attempt to write an allocator that uses some sort of size-based memory pool, like a simple_segregated_storage pool that only returns chunks of a certain size based on sizeof(T)
.
But... is this really true?
I realize the interoperable copy constructor is required for Allocator<T>::rebind
so users of containers don't need to know the internal details of say, a linked-list node type or something. But as far as I can see, the standard itself doesn't seem to say anything so draconian as the requirement that an Allocator<U>
constructed from an Allocator<T>
must compare equal with the original Allocator<T>
instance.
The standard basically requires the following semantics, where X is a type Allocator<T>
, a1 and a2 are instances of X, Y is a type Allocator<U>
, and b is an instance of Allocator<U>
.
From: § 17.6.3.5 (Allocator requirements)
a1 == a2 returns true only if storage allocated from each can be deallocated via the other.
operator == shall be reflexive, symmetric, and transitive, and shall not exit via an exception.
a1 != a2 : same as !(a1 == a2)
a == b : same as a == Y::rebind<T>::other(b)
a != b : same as !(a == b)
X a1(a); Shall not exit via an exception. post: a1 == a
X a(b); Shall not exit via an exception. post: Y(a) == b, a == X(b)
So, the way I read this, instances of Allocator<T>
constructed from Allocator<U>
are not necessarily interchangeable. The standard merely requires that a == b
must be equivalent to Y(a) == b
, not that a == b
must be true!
I think the requirement for the cross-type-boundary copy constructor makes this confusing. But, the way I read this, if I have an Allocator<T>
, it must have a copy constructor that takes an Allocator<U>
, but that doesn't imply that:
Allocator<T> alloc1;
Allocator<U> alloc2(alloc1);
assert(alloc2 == alloc1);
In other words, the way I read this, the above assertion is allowed to fail. But I'm not confident in my understanding here, because:
The accepted answer to this question says otherwise, and the answerer is a guy with 108K reputation
The interaction between copy constructor requirements and equality requirements in the standard is a bit confusing, and I may be misunderstanding the verbiage.
So, I am I correct here? (Incidentally, the implementation of boost::pool_allocator
seems to imply I am correct, assuming the boost developer cares about standards compliance, since this allocator is not interchangeable accross type boundaries.)
rebind
theAllocator<U>
back toT
before deallocating objects of typeT
. – Maudiemaudlinsizeof(T) == some_fixed_size
. – AntimacassarX(Y(a))==a
must still hold. Which means any allocator created froma
must carry the same statea
carries. – Kentigera