I'm trying to implement my custom vector. In some case, I decide to place capacity in front of the real data, and access capacity by shifting the pointer.
Assuming that sizeof(capacity) == 4
while sizeof(value_type) == 512
, using allocator<T>
will cause memory fragment undoubtedly, which might be unacceptable.
Therefore, I can't use allocator<T>::allocate
to allocate the whole chunk memory.
However, I can hold two allocators(allocator<T>
and rebind_alloc<byte>
) at the same time, then allocate memory by rebind_alloc<byte>
and constrcut value by allocator<T>
. I wonder whether it satisfy standard requirements. Or, is there any other good idea?
Holding two allocators for one container might be hard to maintain, so according to my problem, what I need is a stateful allocator.
There is already a ready-made paradigm in the standard library, which is called std::scoped_allocator_adapater
.
The
std::scoped_allocator_adaptor
class template is an allocator which can be used with multilevel containers (vector of sets of lists of tuples of maps, etc). It is instantiated with one outer allocator typeOuterAlloc
and zero or more inner allocator typesInnerAlloc...
. A container constructed directly with ascoped_allocator_adaptor
usesOuterAlloc
to allocate its elements, but if an element is itself a container, it uses the first inner allocator. The elements of that container, if they are themselves containers, use the second inner allocator, etc. If there are more levels to the container than there are inner allocators, the last inner allocator is reused for all further nested containers.
That std::scoped_allocator_adapter
is what I need here. So, I implement my allocator like this:
template <class AllocByte, class AllocTy>
class MyAlloc : AllocTy, AllocByte {
using alty_traits = std::allocator_traits<AllocTy>;
using albyte_traits = std::allocator_traits<AllocByte>;
public:
using alty_type = AllocTy;
using albyte_type = AllocByte;
// other functions and type alias
albyte_type& byte_allocator() noexcept { return static_cast<AllocByte&>(*this); }
const albyte_type& byte_allocator() const noexcept { return static_cast<const AllocByte&>(*this); }
alty_type& value_allocator() noexcept { return static_cast<AllocTy&>(*this); }
const alty_type& value_allocator() const noexcept { return static_cast<const AllocTy&>(*this); }
pointer allocate(size_type n) { return albyte_traits::allocate(byte_allocator(), n); }
void deallocate(pointer ptr, size_type n) { return albyte_traits::deallocate(byte_allocator(), ptr, n); }
size_type max_size() const { return albyte_traits::max_size(byte_allocator()); }
template <class _Ty, class... _Args>
void construct(_Ty* ptr, _Args&&... args) {
alty_traits::construct(value_allocator(), ptr, std::forward<_Args>(args)...);
}
template <class _Ty>
void destroy(_Ty* ptr) {
alty_traits::destroy(value_allocator(), ptr);
}
};