Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allocator Aware Container and propagate_on_container_swap

Tags:

c++

c++11

The std::allocator_traits template defines a few constants, like propagate_on_container_copy/move_assign to let other containers know whether they should copy the allocator of a second container during a copy or move operation.

We also have propagate_on_container_swap, which specifies whether the allocator should be copied during a swap operation.

Is it really necessary for an Allocator Aware container to check for allocator_traits<A>::propagate_on_container_swap in Container::swap()? Usually, I implement swap as follows:

Container::swap(Container& other)
{
   Container tmp(std::move(other));
   other = std::move(*this);
   *this = std::move(tmp);
}

In other words, I simply implement swap in terms of move assignment. Since the move assignment operation already has to deal with allocator awareness (by checking propagate_on_container_move_assign), is it okay to implement Container::swap() like this, instead of writing a totally different swap function which explicitly checks for propagate_on_container_swap?

like image 515
Siler Avatar asked Mar 18 '23 04:03

Siler


1 Answers

It is important to draw a distinction between requirements the standard places on your code vs the requirements it places on types provided by the implementor of the std::lib.

The container requirements specify how the std::containers must behave. However you are free to write your container however you like. Unless you input your container into std::code which requires the std::container behavior, you are good to go. There are just a couple of such places. For example if you adapt your Container with std::stack, then you will have to provide standard behavior if you expect the std::stack to behave according to the standard.

Getting back to your question, if you desire your Container to have the same behavior as one of the std::containers in this regard, then you will have to check and abide by all of the propagate_on traits, and all of the other allocator requirements. This is a non-trivial task. And I am not necessarily recommending it.

The std::containers will not perform a container move construction nor move assignment during swap. They will instead swap their internal representations. They will decide (at compile-time) based on propagate_on_container_swap whether or not they will swap allocators.

If propagate_on_container_swap is true, they will swap allocators and internal representations. Also in this case, swap will be noexcept in C++1z (we hope that is C++17) and forward.

If propagate_on_container_swap is false the allocators shall not be swapped, and need not even be Swappable. However the container internals are still swapped. In this case, if the two allocators do not compare equal, the behavior is undefined.

If you keep your Container::swap as it is, and create a std::stack based on your Container, the std::stack::swap will not have standard behavior. However, it will have the behavior of your Container::swap, and if that is fine with the client of said std::stack and whatever allocator they may be using, then no harm done. std::stack::swap will not behave in mysterious ways just because you didn't rigorously follow all of the intricate details for allocators that the std::containers are required to.

like image 91
Howard Hinnant Avatar answered Mar 23 '23 00:03

Howard Hinnant