Fixes #280 ("Several containers don't support non-movable types when move assigning")

This commit is contained in:
Ion Gaztañaga
2024-05-23 23:28:54 +02:00
parent c38fe90e9a
commit 397fbda8be
26 changed files with 729 additions and 362 deletions
+141 -58
View File
@@ -512,20 +512,21 @@ class devector
const devector &x = rhs;
if (this == &x) { return *this; } // skip self
BOOST_IF_CONSTEXPR(allocator_traits_type::propagate_on_container_copy_assignment::value)
const bool do_propagate = allocator_traits_type::propagate_on_container_copy_assignment::value;
BOOST_IF_CONSTEXPR(do_propagate)
{
allocator_type &this_alloc = this->get_allocator_ref();
const allocator_type &other_alloc = x.get_allocator_ref();
if (this_alloc != other_alloc)
{
// new allocator cannot free existing storage
this->clear();
this->deallocate_buffer();
m_.capacity = 0u;
m_.buffer = pointer();
}
this_alloc = other_alloc;
allocator_type &this_alloc = this->get_allocator_ref();
const allocator_type &other_alloc = x.get_allocator_ref();
if (this_alloc != other_alloc)
{
// new allocator cannot free existing storage
this->clear();
this->deallocate_buffer();
m_.capacity = 0u;
m_.buffer = pointer();
}
dtl::bool_<do_propagate> flag;
dtl::assign_alloc(this_alloc, other_alloc, flag);
}
size_type n = x.size();
@@ -565,53 +566,16 @@ class devector
BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value
|| allocator_traits_type::is_always_equal::value)
{
BOOST_CONSTEXPR_OR_CONST bool copy_alloc = allocator_traits_type::propagate_on_container_move_assignment::value;
BOOST_IF_CONSTEXPR (copy_alloc || get_allocator_ref() == x.get_allocator_ref())
{
this->clear();
this->deallocate_buffer();
if (copy_alloc)
{
this->get_allocator_ref() = boost::move(x.get_allocator_ref());
}
m_.capacity = x.m_.capacity;
m_.buffer = x.m_.buffer;
m_.front_idx = x.m_.front_idx;
m_.back_idx = x.m_.back_idx;
// leave x in valid state
x.m_.capacity = 0u;
x.m_.buffer = pointer();
x.m_.back_idx = x.m_.front_idx = 0;
if (BOOST_LIKELY(this != &x)) {
//We know resources can be transferred at comiple time if both allocators are
//always equal or the allocator is going to be propagated
const bool can_steal_resources_alloc
= allocator_traits_type::propagate_on_container_move_assignment::value
|| allocator_traits_type::is_always_equal::value;
dtl::bool_<can_steal_resources_alloc> flag;
this->priv_move_assign(boost::move(x), flag);
}
else
{
// if the allocator shouldn't be copied and they do not compare equal
// we can't steal memory.
move_iterator<iterator> xbegin = boost::make_move_iterator(x.begin());
move_iterator<iterator> xend = boost::make_move_iterator(x.end());
if (copy_alloc)
{
get_allocator_ref() = boost::move(x.get_allocator_ref());
}
if (m_.capacity >= x.size())
{
overwrite_buffer(xbegin, xend);
}
else
{
allocate_and_copy_range(xbegin, xend);
}
}
BOOST_ASSERT(invariants_ok());
return *this;
}
@@ -1309,6 +1273,78 @@ class devector
return m_.buffer[m_.front_idx + n];
}
//! <b>Requires</b>: size() >= n.
//!
//! <b>Effects</b>: Returns an iterator to the nth element
//! from the beginning of the container. Returns end()
//! if n == size().
//!
//! <b>Throws</b>: Nothing.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Note</b>: Non-standard extension
BOOST_CONTAINER_ATTRIBUTE_NODISCARD inline
iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW
{
BOOST_ASSERT(n <= size());
return iterator(m_.buffer + (m_.front_idx + n));
}
//! <b>Requires</b>: size() >= n.
//!
//! <b>Effects</b>: Returns a const_iterator to the nth element
//! from the beginning of the container. Returns end()
//! if n == size().
//!
//! <b>Throws</b>: Nothing.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Note</b>: Non-standard extension
BOOST_CONTAINER_ATTRIBUTE_NODISCARD inline
const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW
{
BOOST_ASSERT(n <= size());
return const_iterator(m_.buffer + (m_.front_idx + n));
}
//! <b>Requires</b>: begin() <= p <= end().
//!
//! <b>Effects</b>: Returns the index of the element pointed by p
//! and size() if p == end().
//!
//! <b>Throws</b>: Nothing.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Note</b>: Non-standard extension
BOOST_CONTAINER_ATTRIBUTE_NODISCARD inline
size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW
{
BOOST_ASSERT(p >= begin());
BOOST_ASSERT(p <= end());
return static_cast<size_type>(p - this->begin());
}
//! <b>Requires</b>: begin() <= p <= end().
//!
//! <b>Effects</b>: Returns the index of the element pointed by p
//! and size() if p == end().
//!
//! <b>Throws</b>: Nothing.
//!
//! <b>Complexity</b>: Constant.
//!
//! <b>Note</b>: Non-standard extension
BOOST_CONTAINER_ATTRIBUTE_NODISCARD inline
size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW
{
BOOST_ASSERT(p >= cbegin());
BOOST_ASSERT(p <= cend());
return static_cast<size_type>(p - this->cbegin());
}
/**
* **Returns**: A reference to the `n`th element in the devector.
*
@@ -2136,6 +2172,53 @@ class devector
private:
void priv_move_assign(BOOST_RV_REF(devector) x, dtl::bool_<true> /*steal_resources*/)
{
this->clear();
this->deallocate_buffer();
//Move allocator if needed
dtl::bool_<allocator_traits_type::
propagate_on_container_move_assignment::value> flag;
dtl::move_alloc(this->get_allocator_ref(), x.get_allocator_ref(), flag);
m_.capacity = x.m_.capacity;
m_.buffer = x.m_.buffer;
m_.front_idx = x.m_.front_idx;
m_.back_idx = x.m_.back_idx;
// leave x in valid state
x.m_.capacity = 0u;
x.m_.buffer = pointer();
x.m_.back_idx = x.m_.front_idx = 0;
}
void priv_move_assign(BOOST_RV_REF(devector) x, dtl::bool_<false> /*steal_resources*/)
{
//We can't guarantee a compile-time equal allocator or propagation so fallback to runtime
//Resources can be transferred if both allocators are equal
if (get_allocator_ref() == x.get_allocator_ref()) {
this->priv_move_assign(boost::move(x), dtl::true_());
}
else {
// We can't steal memory.
move_iterator<iterator> xbegin = boost::make_move_iterator(x.begin());
move_iterator<iterator> xend = boost::make_move_iterator(x.end());
//Move allocator if needed
dtl::bool_<allocator_traits_type::
propagate_on_container_move_assignment::value> flag;
dtl::move_alloc(this->get_allocator_ref(), x.get_allocator_ref(), flag);
if (m_.capacity >= x.size()) {
overwrite_buffer(xbegin, xend);
}
else {
allocate_and_copy_range(xbegin, xend);
}
}
}
BOOST_CONTAINER_ATTRIBUTE_NODISCARD inline
size_type pos_to_index(const_iterator i) const
{