As far as I understand, std::scoped_allocator_adapter
provides a control mechanism for specifying separately which allocator will be used by a container, by its elements, by elements of their elements, etc. assuming the elements themselves are containers.
That said, I am having trouble understanding the semantics of std::scoped_allocator_adapter
.
Bjarne Stroustrup provides the following 4 examples in The C++ Programming Language, section 34.4.4, pg. 1001(I will refer to them as Example-1, Example-2, etc. in my questions to follow.):
We have four alternatives for allocation of vectors of strings:
// vector and string use their own (the default) allocator: using svec0 = vector<string>; svec0 v0; // vector (only) uses My_alloc and string uses its own allocator (the default): using Svec1 = vector<string,My_alloc<string>>; Svec1 v1 {My_alloc<string>{my_arena1}}; // vector and string use My_alloc (as above): using Xstring = basic_string<char,char_traits<char>, My_alloc<char>>; using Svec2 = vector<Xstring,scoped_allocator_adaptor<My_alloc<Xstring>>>; Svec2 v2 {scoped_allocator_adaptor<My_alloc<Xstring>>{my_arena1}}; // vector uses its own alloctor (the default) and string uses My_alloc: using Xstring2 = basic_string<char, char_traits<char>, My_alloc<char>>; using Svec3 = vector<xstring2,scoped_allocator_adaptor<My_alloc<xstring>,My_alloc<char>>>; Svec3 v3 {scoped_allocator_adaptor<My_alloc<xstring2>,My_alloc<char>>{my_arena1}};
For the sake of completeness, My_alloc
is defined in the book like this:
template<typename T>
struct My_alloc { // use an Arena to allocate and deallocate bytes
Arena& a;
My_alloc(Arena& aa) : a(aa) { }
My_alloc() {}
// usual allocator stuff
};
Now, off to my questions:
Question-1: In Example-3, does the elements of Svec2
use My_alloc<char>{my_arena1}
to allocate storage for their elements(of type char
)?
Question-2: If so, does it have anything to do with the fact that Xstring
uses the custom allocator My_alloc<char>
in its definition? IOW, even if Xstring
were defined with another allocator, wouldn't the elements of Svec_2
still use My_alloc<char>{my_arena1}
since scoped_allocator_adaptor
of Svec2_
dictates which allocator to use for allocations done by Svec_2
and allocations done by its elements(of type Xstring
)?
Question-3: Consider the code below which is supposed to be a variation on Example-3. Does v2_
elements(of type std::string
in this case) use My_alloc<char>{my_arena1}
? If not, which allocator do they use?
// both v2_ and its elements to use My_alloc<some_type>{my_arena1}
using Svec2_ =
vector<string, scoped_allocator_adaptor<My_alloc<string>>> // value_type std::string, instead of Xstring
Svec2_ v2_ {scoped_allocator_adaptor<My_alloc<string>>{my_arena1}};
Question 4: Based on the explanation on www.cppreference.com I quoted below, it seems to me that in Example-4, Svec3
does not use its default allocator std::allocator
to allocate storage for its elements(of Xstring2
type), but rather the "outer allocator", which is My_alloc<xstring>
. Elements of Svec3
, on the other hand, will use MyAlloc<char>
which is the "inner allocator". (And not because it is the allocator specified in the definition of Xstring2
, but because it is the "inner allocator" in scoped_allocator_adapter
of Svec3
.)
This is what it says on www.cppreference.com in std::scoped_allocator_adaptor
section:
Given:
template< class OuterAlloc, class... InnerAlloc > class scoped_allocator_adaptor : public OuterAlloc;
A container constructed directly with a scoped_allocator_adaptor uses
OuterAlloc
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.
Based on that, shouldn't the correct example be as follows?
// v3_ uses its default allocator(or rather std::allocator), while its elements use My_alloc
using Xstring2 = basic_string<char, char_traits<char>, My_alloc<char>>;
using Svec3_ =
vector<Xstring2, scoped_allocator_adaptor<std::allocator<Xstring2>, My_alloc<char>>>;
Svec3_ v3_ {
scoped_allocator_adaptor<std::allocator<Xstring2>, My_alloc<char>>{my_arena1}
};
This way, doesn't this use of scoped_allocator_adaptor
dictate that allocations by Svec3_
use std::allocator
, while its elements use My_alloc<char>{my_arena1}
for their allocations?
I can tell I have fundamental misunderstandings of the scoped allocator model. Can someone point out what I am missing?
template <typename T> using ScAlloc = std::scoped_allocator_adaptor<MyAlloc<T>>;
. Then you can easily spell things likevector<T, ScAlloc<T>>
. – Correna