Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does propagate_on_XXX_assignment not apply to constructors?

The C++11 std::allocator_traits template is used to query an Allocator to determine whether propagate_on_copy_assignment and propagate_on_move_assignment is true. These values affect how Container types must implement copy and move assignment. If std::allocator_traits<Allocator>::propagate_on_move_assignment == true then a Container move assignment operator must move assign its internal allocator object using the allocator contained in the RHS Container object.

Presumably, the point of this is that so we can implement Allocator types that can inform the client Container whether or not a move or copy operation should require that we copy any internal state inside the Allocator.

So... why do these typedefs only apply to assignment. Why do we not have propagate_on_copy_construct and propagate_on_move_construct? If an allocator has some internal state, shouldn't the client Container object be aware of that so it knows whether or not it should copy/move the allocator?

like image 506
Siler Avatar asked Dec 15 '14 20:12

Siler


1 Answers

In assignment one has two choices:

  1. Keep your existing allocator.
  2. Copy/move from the rhs allocator.

The propagate_on_assign traits controls the choice for the assignment operators.

For construction, choice 1 (keeping your existing allocator) is not an option. There is no existing allocator. Instead your choices are:

  1. Construct an allocator from nothing.
  2. Copy/move from the rhs allocator.
  3. Copy/move from some other source.

Choice one might be considered: default construct an allocator. Choice 3 is pretty vague, but different allocator schemes can be unpredictable and it is good to give the allocator designer as much flexibility as possible: Possibly to do some things that the committee never anticipated.

In an attempt to satisfy all three choices, at least for the container copy constructor, a new allocator_traits "switch" was designed:

Alloc select_on_container_copy_construction(const Alloc& rhs);

allocator_traits will return rhs. select_on_container_copy_construction() if that expression is well-formed. Otherwise it will return rhs. And container copy constructors are required to use allocator_traits<A>:: select_on_container_copy_construction(a) when constructing the lhs allocator during copy construction.

For allocator designers who just want their allocator copied during copy construction (choice 2), they don't have to do anything.

Allocator designers who want to have the lhs allocator default constructed on container copy construction just need to write the following member function for their allocator:

Alloc select_on_container_copy_construction() const {return Alloc{};}

And if they think of something clever for choice 3, they can probably implement it using the same hook as for choice 1.

So for copy construction, there is no need for a propagate_on trait as the select_on_container_copy_construction already covers all bases.

For move construction, see the bottom of page 12 of N2982:

Note that there is no select_on_container_move_construction() function. After some consideration, we decided that a move construction operation for containers must run in constant-time and not throw, as per issue 1166.

Indeed this paper, and papers preceding it (that it references) are a good source of information for the rationale behind the C++11 allocator design. This paper even references a allocator_propagate_on_copy_construction from a previous paper, which subsequently evolved into select_on_container_copy_construction().

like image 78
Howard Hinnant Avatar answered Oct 08 '22 06:10

Howard Hinnant