Fixes #88 ("Implement C++17 MoveAssignable requirements for self-move assignments")

This commit is contained in:
Ion Gaztañaga
2019-04-30 18:05:50 +02:00
parent 909ccf6057
commit 5f0ce60b39
7 changed files with 151 additions and 146 deletions

View File

@@ -1267,9 +1267,9 @@ use [*Boost.Container]? There are several reasons for that:
[section:release_notes Release Notes] [section:release_notes Release Notes]
[section:release_notes_boost_1_71_00 Boost 1.71 Release] [section:release_notes_boost_1_71_00 Boost 1.71 Release]
* [@https://github.com/boostorg/container/issues/88 GitHub #88: ['"Implement C++17 MoveAssignable requirements for self-move assignments"]].
* [@https://github.com/boostorg/container/pull/109 GitHub #109: ['"Get rid of integer overflow in copy_move_algo.hpp (-fsanitize=integer)"]]. * [@https://github.com/boostorg/container/pull/109 GitHub #109: ['"Get rid of integer overflow in copy_move_algo.hpp (-fsanitize=integer)"]].
* [@https://github.com/boostorg/container/pull/110 GitHub #110: ['"Avoid gcc 9 deprecated copy warnings in new_allocator.hpp"]]. * [@https://github.com/boostorg/container/pull/110 GitHub #110: ['"Avoid gcc 9 deprecated copy warnings in new_allocator.hpp"]].
* [@https://github.com/boostorg/container/issues/112 GitHub #112: ['"vector::resize() compilation error with msvc-10..12: data is not a member of boost::detail::aligned_storage"]]. * [@https://github.com/boostorg/container/issues/112 GitHub #112: ['"vector::resize() compilation error with msvc-10..12: data is not a member of boost::detail::aligned_storage"]].
* [@https://github.com/boostorg/container/issues/114 GitHub #114: ['"Fix small_vector noexcept specification"]]. * [@https://github.com/boostorg/container/issues/114 GitHub #114: ['"Fix small_vector noexcept specification"]].
* [@https://github.com/boostorg/container/issues/116 GitHub #116: ['"MSVC + boost 1.70 compilation error when windows.h is already included (detail/thread_mutex.hpp)"]]. * [@https://github.com/boostorg/container/issues/116 GitHub #116: ['"MSVC + boost 1.70 compilation error when windows.h is already included (detail/thread_mutex.hpp)"]].

View File

@@ -811,7 +811,7 @@ class deque : protected deque_base<typename real_allocator<T, Allocator>::type,
//! <b>Complexity</b>: Linear to the number of elements in x. //! <b>Complexity</b>: Linear to the number of elements in x.
deque& operator= (BOOST_COPY_ASSIGN_REF(deque) x) deque& operator= (BOOST_COPY_ASSIGN_REF(deque) x)
{ {
if (&x != this){ if (BOOST_LIKELY(&x != this)){
allocator_type &this_alloc = this->alloc(); allocator_type &this_alloc = this->alloc();
const allocator_type &x_alloc = x.alloc(); const allocator_type &x_alloc = x.alloc();
dtl::bool_<allocator_traits_type:: dtl::bool_<allocator_traits_type::
@@ -839,28 +839,29 @@ class deque : protected deque_base<typename real_allocator<T, Allocator>::type,
BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value
|| allocator_traits_type::is_always_equal::value) || allocator_traits_type::is_always_equal::value)
{ {
BOOST_ASSERT(this != &x); if (BOOST_LIKELY(this != &x)) {
allocator_type &this_alloc = this->alloc(); allocator_type &this_alloc = this->alloc();
allocator_type &x_alloc = x.alloc(); allocator_type &x_alloc = x.alloc();
const bool propagate_alloc = allocator_traits_type:: const bool propagate_alloc = allocator_traits_type::
propagate_on_container_move_assignment::value; propagate_on_container_move_assignment::value;
dtl::bool_<propagate_alloc> flag; dtl::bool_<propagate_alloc> flag;
const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal; const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal;
//Resources can be transferred if both allocators are //Resources can be transferred if both allocators are
//going to be equal after this function (either propagated or already equal) //going to be equal after this function (either propagated or already equal)
if(propagate_alloc || allocators_equal){ if(propagate_alloc || allocators_equal){
//Destroy objects but retain memory in case x reuses it in the future //Destroy objects but retain memory in case x reuses it in the future
this->clear(); this->clear();
//Move allocator if needed //Move allocator if needed
dtl::move_alloc(this_alloc, x_alloc, flag); dtl::move_alloc(this_alloc, x_alloc, flag);
dtl::move_alloc(this->ptr_alloc(), x.ptr_alloc(), flag); dtl::move_alloc(this->ptr_alloc(), x.ptr_alloc(), flag);
//Nothrow swap //Nothrow swap
this->swap_members(x); this->swap_members(x);
} }
//Else do a one by one move //Else do a one by one move
else{ else{
this->assign( boost::make_move_iterator(x.begin()) this->assign( boost::make_move_iterator(x.begin())
, boost::make_move_iterator(x.end())); , boost::make_move_iterator(x.end()));
}
} }
return *this; return *this;
} }

View File

@@ -788,7 +788,7 @@ class tree
tree& operator=(BOOST_COPY_ASSIGN_REF(tree) x) tree& operator=(BOOST_COPY_ASSIGN_REF(tree) x)
{ {
if (&x != this){ if (BOOST_LIKELY(this != &x)) {
NodeAlloc &this_alloc = this->get_stored_allocator(); NodeAlloc &this_alloc = this->get_stored_allocator();
const NodeAlloc &x_alloc = x.get_stored_allocator(); const NodeAlloc &x_alloc = x.get_stored_allocator();
dtl::bool_<allocator_traits<NodeAlloc>:: dtl::bool_<allocator_traits<NodeAlloc>::
@@ -822,39 +822,40 @@ class tree
allocator_traits_type::is_always_equal::value) && allocator_traits_type::is_always_equal::value) &&
boost::container::dtl::is_nothrow_move_assignable<Compare>::value) boost::container::dtl::is_nothrow_move_assignable<Compare>::value)
{ {
BOOST_ASSERT(this != &x); if (BOOST_LIKELY(this != &x)) {
NodeAlloc &this_alloc = this->node_alloc(); NodeAlloc &this_alloc = this->node_alloc();
NodeAlloc &x_alloc = x.node_alloc(); NodeAlloc &x_alloc = x.node_alloc();
const bool propagate_alloc = allocator_traits<NodeAlloc>:: const bool propagate_alloc = allocator_traits<NodeAlloc>::
propagate_on_container_move_assignment::value; propagate_on_container_move_assignment::value;
const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal; const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal;
//Resources can be transferred if both allocators are //Resources can be transferred if both allocators are
//going to be equal after this function (either propagated or already equal) //going to be equal after this function (either propagated or already equal)
if(propagate_alloc || allocators_equal){ if(propagate_alloc || allocators_equal){
//Destroy //Destroy
this->clear(); this->clear();
//Move allocator if needed //Move allocator if needed
this->AllocHolder::move_assign_alloc(x); this->AllocHolder::move_assign_alloc(x);
//Obtain resources //Obtain resources
this->icont() = boost::move(x.icont()); this->icont() = boost::move(x.icont());
} }
//Else do a one by one move //Else do a one by one move
else{ else{
//Transfer all the nodes to a temporary tree //Transfer all the nodes to a temporary tree
//If anything goes wrong, all the nodes will be destroyed //If anything goes wrong, all the nodes will be destroyed
//automatically //automatically
Icont other_tree(::boost::move(this->icont())); Icont other_tree(::boost::move(this->icont()));
//Now recreate the source tree reusing nodes stored by other_tree //Now recreate the source tree reusing nodes stored by other_tree
this->icont().clone_from this->icont().clone_from
(::boost::move(x.icont()) (::boost::move(x.icont())
, RecyclingCloner<AllocHolder, true>(*this, other_tree) , RecyclingCloner<AllocHolder, true>(*this, other_tree)
, Destroyer(this->node_alloc())); , Destroyer(this->node_alloc()));
//If there are remaining nodes, destroy them //If there are remaining nodes, destroy them
NodePtr p; NodePtr p;
while((p = other_tree.unlink_leftmost_without_rebalance())){ while((p = other_tree.unlink_leftmost_without_rebalance())){
AllocHolder::destroy_node(p); AllocHolder::destroy_node(p);
}
} }
} }
return *this; return *this;

View File

@@ -367,7 +367,7 @@ class list
//! <b>Complexity</b>: Linear to the number of elements in x. //! <b>Complexity</b>: Linear to the number of elements in x.
list& operator=(BOOST_COPY_ASSIGN_REF(list) x) list& operator=(BOOST_COPY_ASSIGN_REF(list) x)
{ {
if (&x != this){ if (BOOST_LIKELY(this != &x)) {
NodeAlloc &this_alloc = this->node_alloc(); NodeAlloc &this_alloc = this->node_alloc();
const NodeAlloc &x_alloc = x.node_alloc(); const NodeAlloc &x_alloc = x.node_alloc();
dtl::bool_<allocator_traits_type:: dtl::bool_<allocator_traits_type::
@@ -396,26 +396,27 @@ class list
BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value
|| allocator_traits_type::is_always_equal::value) || allocator_traits_type::is_always_equal::value)
{ {
BOOST_ASSERT(this != &x); if (BOOST_LIKELY(this != &x)) {
NodeAlloc &this_alloc = this->node_alloc(); NodeAlloc &this_alloc = this->node_alloc();
NodeAlloc &x_alloc = x.node_alloc(); NodeAlloc &x_alloc = x.node_alloc();
const bool propagate_alloc = allocator_traits_type:: const bool propagate_alloc = allocator_traits_type::
propagate_on_container_move_assignment::value; propagate_on_container_move_assignment::value;
const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal; const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal;
//Resources can be transferred if both allocators are //Resources can be transferred if both allocators are
//going to be equal after this function (either propagated or already equal) //going to be equal after this function (either propagated or already equal)
if(propagate_alloc || allocators_equal){ if(propagate_alloc || allocators_equal){
//Destroy //Destroy
this->clear(); this->clear();
//Move allocator if needed //Move allocator if needed
this->AllocHolder::move_assign_alloc(x); this->AllocHolder::move_assign_alloc(x);
//Obtain resources //Obtain resources
this->icont() = boost::move(x.icont()); this->icont() = boost::move(x.icont());
} }
//Else do a one by one move //Else do a one by one move
else{ else{
this->assign( boost::make_move_iterator(x.begin()) this->assign( boost::make_move_iterator(x.begin())
, boost::make_move_iterator(x.end())); , boost::make_move_iterator(x.end()));
}
} }
return *this; return *this;
} }

View File

@@ -395,7 +395,7 @@ class slist
//! <b>Complexity</b>: Linear to the number of elements in x. //! <b>Complexity</b>: Linear to the number of elements in x.
slist& operator= (BOOST_COPY_ASSIGN_REF(slist) x) slist& operator= (BOOST_COPY_ASSIGN_REF(slist) x)
{ {
if (&x != this){ if (BOOST_LIKELY(this != &x)) {
NodeAlloc &this_alloc = this->node_alloc(); NodeAlloc &this_alloc = this->node_alloc();
const NodeAlloc &x_alloc = x.node_alloc(); const NodeAlloc &x_alloc = x.node_alloc();
dtl::bool_<allocator_traits_type:: dtl::bool_<allocator_traits_type::
@@ -424,26 +424,27 @@ class slist
BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value
|| allocator_traits_type::is_always_equal::value) || allocator_traits_type::is_always_equal::value)
{ {
BOOST_ASSERT(this != &x); if (BOOST_LIKELY(this != &x)) {
NodeAlloc &this_alloc = this->node_alloc(); NodeAlloc &this_alloc = this->node_alloc();
NodeAlloc &x_alloc = x.node_alloc(); NodeAlloc &x_alloc = x.node_alloc();
const bool propagate_alloc = allocator_traits_type:: const bool propagate_alloc = allocator_traits_type::
propagate_on_container_move_assignment::value; propagate_on_container_move_assignment::value;
const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal; const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal;
//Resources can be transferred if both allocators are //Resources can be transferred if both allocators are
//going to be equal after this function (either propagated or already equal) //going to be equal after this function (either propagated or already equal)
if(propagate_alloc || allocators_equal){ if(propagate_alloc || allocators_equal){
//Destroy //Destroy
this->clear(); this->clear();
//Move allocator if needed //Move allocator if needed
this->AllocHolder::move_assign_alloc(x); this->AllocHolder::move_assign_alloc(x);
//Obtain resources //Obtain resources
this->icont() = boost::move(x.icont()); this->icont() = boost::move(x.icont());
} }
//Else do a one by one move //Else do a one by one move
else{ else{
this->assign( boost::make_move_iterator(x.begin()) this->assign( boost::make_move_iterator(x.begin())
, boost::make_move_iterator(x.end())); , boost::make_move_iterator(x.end()));
}
} }
return *this; return *this;
} }

View File

@@ -823,7 +823,7 @@ class stable_vector
stable_vector& operator=(BOOST_COPY_ASSIGN_REF(stable_vector) x) stable_vector& operator=(BOOST_COPY_ASSIGN_REF(stable_vector) x)
{ {
STABLE_VECTOR_CHECK_INVARIANT; STABLE_VECTOR_CHECK_INVARIANT;
if (&x != this){ if (BOOST_LIKELY(this != &x)) {
node_allocator_type &this_alloc = this->priv_node_alloc(); node_allocator_type &this_alloc = this->priv_node_alloc();
const node_allocator_type &x_alloc = x.priv_node_alloc(); const node_allocator_type &x_alloc = x.priv_node_alloc();
dtl::bool_<allocator_traits_type:: dtl::bool_<allocator_traits_type::
@@ -855,29 +855,30 @@ class stable_vector
|| allocator_traits_type::is_always_equal::value) || allocator_traits_type::is_always_equal::value)
{ {
//for move constructor, no aliasing (&x != this) is assumed. //for move constructor, no aliasing (&x != this) is assumed.
BOOST_ASSERT(this != &x); if (BOOST_LIKELY(this != &x)) {
node_allocator_type &this_alloc = this->priv_node_alloc(); node_allocator_type &this_alloc = this->priv_node_alloc();
node_allocator_type &x_alloc = x.priv_node_alloc(); node_allocator_type &x_alloc = x.priv_node_alloc();
const bool propagate_alloc = allocator_traits_type:: const bool propagate_alloc = allocator_traits_type::
propagate_on_container_move_assignment::value; propagate_on_container_move_assignment::value;
dtl::bool_<propagate_alloc> flag; dtl::bool_<propagate_alloc> flag;
const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal; const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal;
//Resources can be transferred if both allocators are //Resources can be transferred if both allocators are
//going to be equal after this function (either propagated or already equal) //going to be equal after this function (either propagated or already equal)
if(propagate_alloc || allocators_equal){ if(propagate_alloc || allocators_equal){
STABLE_VECTOR_CHECK_INVARIANT STABLE_VECTOR_CHECK_INVARIANT
//Destroy objects but retain memory in case x reuses it in the future //Destroy objects but retain memory in case x reuses it in the future
this->clear(); this->clear();
//Move allocator if needed //Move allocator if needed
dtl::move_alloc(this_alloc, x_alloc, flag); dtl::move_alloc(this_alloc, x_alloc, flag);
//Take resources //Take resources
this->index.swap(x.index); this->index.swap(x.index);
this->priv_swap_members(x); this->priv_swap_members(x);
} }
//Else do a one by one move //Else do a one by one move
else{ else{
this->assign( boost::make_move_iterator(x.begin()) this->assign( boost::make_move_iterator(x.begin())
, boost::make_move_iterator(x.end())); , boost::make_move_iterator(x.end()));
}
} }
return *this; return *this;
} }

View File

@@ -865,7 +865,7 @@ class basic_string
//! <b>Complexity</b>: Linear to the elements x contains. //! <b>Complexity</b>: Linear to the elements x contains.
basic_string& operator=(BOOST_COPY_ASSIGN_REF(basic_string) x) basic_string& operator=(BOOST_COPY_ASSIGN_REF(basic_string) x)
{ {
if (&x != this){ if (BOOST_LIKELY(this != &x)) {
allocator_type &this_alloc = this->alloc(); allocator_type &this_alloc = this->alloc();
const allocator_type &x_alloc = x.alloc(); const allocator_type &x_alloc = x.alloc();
dtl::bool_<allocator_traits_type:: dtl::bool_<allocator_traits_type::
@@ -896,27 +896,27 @@ class basic_string
BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value
|| allocator_traits_type::is_always_equal::value) || allocator_traits_type::is_always_equal::value)
{ {
//for move constructor, no aliasing (&x != this) is assumed. if (BOOST_LIKELY(this != &x)) {
BOOST_ASSERT(this != &x); allocator_type &this_alloc = this->alloc();
allocator_type &this_alloc = this->alloc(); allocator_type &x_alloc = x.alloc();
allocator_type &x_alloc = x.alloc(); const bool propagate_alloc = allocator_traits_type::
const bool propagate_alloc = allocator_traits_type:: propagate_on_container_move_assignment::value;
propagate_on_container_move_assignment::value; dtl::bool_<propagate_alloc> flag;
dtl::bool_<propagate_alloc> flag; const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal;
const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal; //Resources can be transferred if both allocators are
//Resources can be transferred if both allocators are //going to be equal after this function (either propagated or already equal)
//going to be equal after this function (either propagated or already equal) if(propagate_alloc || allocators_equal){
if(propagate_alloc || allocators_equal){ //Destroy objects but retain memory in case x reuses it in the future
//Destroy objects but retain memory in case x reuses it in the future this->clear();
this->clear(); //Move allocator if needed
//Move allocator if needed dtl::move_alloc(this_alloc, x_alloc, flag);
dtl::move_alloc(this_alloc, x_alloc, flag); //Nothrow swap
//Nothrow swap this->swap_data(x);
this->swap_data(x); }
} //Else do a one by one move
//Else do a one by one move else{
else{ this->assign( x.begin(), x.end());
this->assign( x.begin(), x.end()); }
} }
return *this; return *this;
} }