Difference between revisions of "cpp/memory/scoped allocator adaptor"
Andreas Krug (Talk | contribs) m (fmt) |
(Updated the nested type list.) |
||
Line 15: | Line 15: | ||
Typical implementation holds an instance of a {{tt|std::scoped_allocator_adaptor<InnerAllocs...>}} as a member object. | Typical implementation holds an instance of a {{tt|std::scoped_allocator_adaptor<InnerAllocs...>}} as a member object. | ||
− | Note that {{ltt|cpp/memory/polymorphic_allocator|std::pmr:: | + | Note that {{ltt|cpp/memory/polymorphic_allocator|std::pmr::polymorphic_allocator}}s propagate to nested containers following {{lsd|cpp/memory/uses allocator#Uses-allocator construction}} and do not need (and do not work with) {{tt|std::scoped_allocator_adaptor}}. |
− | === | + | ===Nested types=== |
{{dsc begin}} | {{dsc begin}} | ||
{{dsc hitem|Type|Definition}} | {{dsc hitem|Type|Definition}} | ||
− | {{dsc|{{tt|outer_allocator_type}}|{{ | + | {{dsc|{{tt|outer_allocator_type}}|{{tt|OuterAlloc}}}} |
− | {{dsc|{{tt|inner_allocator_type}}|{{c|scoped_allocator_adaptor<InnerAllocs...>}} | + | {{dsc|{{tt|inner_allocator_type}}| |
− | {{dsc|{{tt|value_type}}|{{c|std::allocator_traits<OuterAlloc>::value_type}}}} | + | * {{c/core|scoped_allocator_adaptor<InnerAllocs...>}} if {{c|1=sizeof...(InnerAllocs)}} is zero |
− | {{dsc|{{tt|size_type}}|{{c|std::allocator_traits<OuterAlloc>::size_type}}}} | + | * {{c/core|scoped_allocator_adaptor<OuterAlloc>}} otherwise}} |
− | {{dsc|{{tt|difference_type}}|{{c|std::allocator_traits<OuterAlloc>::difference_type}}}} | + | {{dsc|{{tt|value_type}}|{{c/core|std::allocator_traits<OuterAlloc>::value_type}}}} |
− | {{dsc|{{tt|pointer}}|{{c|std::allocator_traits<OuterAlloc>::pointer}}}} | + | {{dsc|{{tt|size_type}}|{{c/core|std::allocator_traits<OuterAlloc>::size_type}}}} |
− | {{dsc|{{tt|const_pointer}}|{{c|std::allocator_traits<OuterAlloc>::const_pointer}}}} | + | {{dsc|{{tt|difference_type}}|{{c/core|std::allocator_traits<OuterAlloc>::difference_type}}}} |
− | {{dsc|{{tt|void_pointer}}|{{c|std::allocator_traits<OuterAlloc>::void_pointer}}}} | + | {{dsc|{{tt|pointer}}|{{c/core|std::allocator_traits<OuterAlloc>::pointer}}}} |
− | {{dsc|{{tt|const_void_pointer}}|{{c|std::allocator_traits<OuterAlloc>::const_void_pointer}}}} | + | {{dsc|{{tt|const_pointer}}|{{c/core|std::allocator_traits<OuterAlloc>::const_pointer}}}} |
− | {{dsc | + | {{dsc|{{tt|void_pointer}}|{{c/core|std::allocator_traits<OuterAlloc>::void_pointer}}}} |
− | + | {{dsc|{{tt|const_void_pointer}}|{{c/core|std::allocator_traits<OuterAlloc>::const_void_pointer}}}} | |
− | {{dsc|{{tt| | + | {{dsc end}} |
− | + | ||
− | {{dsc|{{tt| | + | |
− | + | Given the set of {{tt|OuterAlloc}} and {{tt|InnerAlloc...}} as {{tt|Allocs}}: | |
− | {{dsc|{{tt| | + | {{dsc begin}} |
− | + | {{dsc hitem|Type|Definition}} | |
− | {{ | + | {{dsc|{{tt|propagate_on_container_copy_assignment}}| |
− | {{ | + | * {{lc|std::true_type}} if there exists a type {{tt|A}} in {{tt|Allocs}} such that {{c multi|std::allocator_traits<A>::| propagate_on_container_copy_assignment::value}} is {{c|true}} |
− | + | * {{lc|std::false_type}} otherwise}} | |
− | + | {{dsc|{{tt|propagate_on_container_move_assignment}}| | |
− | + | * {{lc|std::true_type}} if there exists a type {{tt|A}} in {{tt|Allocs}} such that {{c multi|std::allocator_traits<A>::| propagate_on_container_move_assignment::value}} is {{c|true}} | |
− | + | * {{lc|std::false_type}} otherwise}} | |
− | + | {{dsc|{{tt|propagate_on_container_swap}}| | |
− | + | * {{lc|std::true_type}} if there exists a type {{tt|A}} in {{tt|Allocs}} such that {{c multi|std::allocator_traits<A>::| propagate_on_container_swap::value}} is {{c|true}} | |
− | + | * {{lc|std::false_type}} otherwise}} | |
− | } | + | {{dsc|{{tt|is_always_equal}}| |
− | }} | + | * {{lc|std::true_type}} if there exists a type {{tt|A}} in {{tt|Allocs}} such that {{c multi|std::allocator_traits<A>::| is_always_equal::value}} is {{c|true}} |
− | }} | + | * {{lc|std::false_type}} otherwise}} |
{{dsc end}} | {{dsc end}} | ||
Line 68: | Line 68: | ||
===Non-member functions=== | ===Non-member functions=== | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc inc|cpp/memory/scoped_allocator_adaptor/dsc | + | {{dsc inc|cpp/memory/scoped_allocator_adaptor/dsc operator cmp}} |
{{dsc end}} | {{dsc end}} | ||
− | ===[[cpp/memory/scoped_allocator_adaptor/ | + | ===[[cpp/memory/scoped_allocator_adaptor/deduction guides|Deduction guides]]{{mark since c++17}}=== |
+ | |||
+ | ===Nested classes=== | ||
+ | {{dsc begin}} | ||
+ | {{dsc hitem|Class|Definition}} | ||
+ | {{dsc|{{tt|rebind}}{{nbsp}}|{{c| | ||
+ | template< class T > | ||
+ | struct rebind | ||
+ | { | ||
+ | using other {{=}} scoped_allocator_adaptor | ||
+ | <std::allocator_traits<OuterAlloc>::template rebind_alloc<T>, | ||
+ | InnerAllocs...>; | ||
+ | }; | ||
+ | }} | ||
+ | }} | ||
+ | {{dsc end}} | ||
===Example=== | ===Example=== | ||
Line 83: | Line 98: | ||
namespace bi = boost::interprocess; | namespace bi = boost::interprocess; | ||
− | template<class T> using alloc = bi::adaptive_pool<T, | + | template<class T> |
− | + | using alloc = bi::adaptive_pool<T, bi::managed_shared_memory::segment_manager>; | |
+ | |||
using ipc_row = std::vector<int, alloc<int>>; | using ipc_row = std::vector<int, alloc<int>>; | ||
+ | |||
using ipc_matrix = std::vector<ipc_row, std::scoped_allocator_adaptor<alloc<ipc_row>>>; | using ipc_matrix = std::vector<ipc_row, std::scoped_allocator_adaptor<alloc<ipc_row>>>; | ||
Line 91: | Line 108: | ||
{ | { | ||
bi::managed_shared_memory s(bi::create_only, "Demo", 65536); | bi::managed_shared_memory s(bi::create_only, "Demo", 65536); | ||
− | + | ||
// create vector of vectors in shared memory | // create vector of vectors in shared memory | ||
ipc_matrix v(s.get_segment_manager()); | ipc_matrix v(s.get_segment_manager()); | ||
− | + | ||
// for all these additions, the inner vectors obtain their allocator arguments | // for all these additions, the inner vectors obtain their allocator arguments | ||
// from the outer vector's scoped_allocator_adaptor | // from the outer vector's scoped_allocator_adaptor | ||
Line 102: | Line 119: | ||
std::vector<int> local_row = {1, 2, 3}; | std::vector<int> local_row = {1, 2, 3}; | ||
v.emplace_back(local_row.begin(), local_row.end()); | v.emplace_back(local_row.begin(), local_row.end()); | ||
− | + | ||
bi::shared_memory_object::remove("Demo"); | bi::shared_memory_object::remove("Demo"); | ||
} | } | ||
Line 109: | Line 126: | ||
===Defect reports=== | ===Defect reports=== | ||
{{dr list begin}} | {{dr list begin}} | ||
− | {{dr list item|wg=lwg|std=C++11|dr=2108|before=there was no way to show if {{tt|scoped_allocator_adaptor}} is stateless|after={{tt|is_always_equal}} | + | {{dr list item|wg=lwg|std=C++11|dr=2108|before=there was no way to show if {{tt|scoped_allocator_adaptor}} is stateless|after=provided {{tt|is_always_equal}}}} |
{{dr list end}} | {{dr list end}} | ||
Revision as of 23:29, 12 November 2024
Defined in header <scoped_allocator>
|
||
template< class OuterAlloc, class... InnerAllocs > class scoped_allocator_adaptor |
(since C++11) | |
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 type OuterAlloc
and zero or more inner allocator types InnerAlloc...
. 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.
The purpose of this adaptor is to correctly initialize stateful allocators in nested containers, such as when all levels of a nested container must be placed in the same shared memory segment. The adaptor's constructor takes the arguments for all allocators in the list, and each nested container obtains its allocator's state from the adaptor as needed.
For the purpose of scoped_allocator_adaptor
, if the next inner allocator is A
, any class T
for which std::uses_allocator<T,A>::value == true participates in the recursion as if it was a container. Additionally, std::pair is treated as such a container by specific overloads of scoped_allocator_adaptor::construct.
Typical implementation holds an instance of a std::scoped_allocator_adaptor<InnerAllocs...>
as a member object.
Note that std::pmr::polymorphic_allocators propagate to nested containers following uses-allocator construction and do not need (and do not work with) std::scoped_allocator_adaptor
.
Contents |
Nested types
Type | Definition |
outer_allocator_type
|
OuterAlloc
|
inner_allocator_type
|
|
value_type
|
std::allocator_traits<OuterAlloc>::value_type |
size_type
|
std::allocator_traits<OuterAlloc>::size_type |
difference_type
|
std::allocator_traits<OuterAlloc>::difference_type |
pointer
|
std::allocator_traits<OuterAlloc>::pointer |
const_pointer
|
std::allocator_traits<OuterAlloc>::const_pointer |
void_pointer
|
std::allocator_traits<OuterAlloc>::void_pointer |
const_void_pointer
|
std::allocator_traits<OuterAlloc>::const_void_pointer |
Given the set of OuterAlloc
and InnerAlloc...
as Allocs
:
Type | Definition |
propagate_on_container_copy_assignment
|
|
propagate_on_container_move_assignment
|
|
propagate_on_container_swap
|
|
is_always_equal
|
|
Member functions
creates a new scoped_allocator_adaptor object (public member function) | |
destructs a scoped_allocator_adaptor object (public member function) | |
assigns a scoped_allocator_adaptor (public member function) | |
obtains an inner_allocator reference (public member function) | |
obtains an outer_allocator reference (public member function) | |
allocates uninitialized storage using the outer allocator (public member function) | |
deallocates storage using the outer allocator (public member function) | |
returns the largest allocation size supported by the outer allocator (public member function) | |
constructs an object in allocated storage, passing the inner allocator to its constructor if appropriate (public member function) | |
destructs an object in allocated storage (public member function) | |
copies the state of scoped_allocator_adaptor and all its allocators (public member function) |
Non-member functions
(removed in C++20) |
compares two scoped_allocator_adaptor objects (function template) |
Deduction guides(since C++17)
Nested classes
Class | Definition |
rebind
|
template< class T > struct rebind |
Example
#include <boost/interprocess/allocators/adaptive_pool.hpp> #include <boost/interprocess/managed_shared_memory.hpp> #include <scoped_allocator> #include <vector> namespace bi = boost::interprocess; template<class T> using alloc = bi::adaptive_pool<T, bi::managed_shared_memory::segment_manager>; using ipc_row = std::vector<int, alloc<int>>; using ipc_matrix = std::vector<ipc_row, std::scoped_allocator_adaptor<alloc<ipc_row>>>; int main () { bi::managed_shared_memory s(bi::create_only, "Demo", 65536); // create vector of vectors in shared memory ipc_matrix v(s.get_segment_manager()); // for all these additions, the inner vectors obtain their allocator arguments // from the outer vector's scoped_allocator_adaptor v.resize(1); v[0].push_back(1); v.emplace_back(2); std::vector<int> local_row = {1, 2, 3}; v.emplace_back(local_row.begin(), local_row.end()); bi::shared_memory_object::remove("Demo"); }
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
LWG 2108 | C++11 | there was no way to show if scoped_allocator_adaptor is stateless
|
provided is_always_equal
|
See also
(C++11) |
provides information about allocator types (class template) |
(C++11) |
checks if the specified type supports uses-allocator construction (class template) |
the default allocator (class template) |