In b-tree, support unassignable value types.

Avoid using value move/swap and delete those functions from slot_policy types. There was only one use of params_type::move in `erase`.

PiperOrigin-RevId: 453237739
Change-Id: Ie81c6dba6c4db34e97a067d2c0defcded8044a5a
pull/1194/head
Evan Brown 3 years ago committed by Copybara-Service
parent 6481443560
commit ef034836d3
  1. 11
      absl/container/btree_set.h
  2. 60
      absl/container/btree_test.cc
  3. 40
      absl/container/internal/btree.h
  4. 27
      absl/container/internal/container_memory.h

@ -766,17 +766,6 @@ struct set_slot_policy {
construct(alloc, new_slot, old_slot);
destroy(alloc, old_slot);
}
template <typename Alloc>
static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) {
using std::swap;
swap(*a, *b);
}
template <typename Alloc>
static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) {
*dest = std::move(*src);
}
};
// A parameters structure for holding the type parameters for a btree_set.

@ -3188,6 +3188,66 @@ TEST(Btree, OnlyConstructibleByAllocatorType) {
}
}
class NotAssignable {
public:
explicit NotAssignable(int i) : i_(i) {}
NotAssignable(const NotAssignable &other) : i_(other.i_) {}
NotAssignable &operator=(NotAssignable &&other) = delete;
int Get() const { return i_; }
bool operator==(int i) const { return i_ == i; }
friend bool operator<(NotAssignable a, NotAssignable b) {
return a.i_ < b.i_;
}
private:
int i_;
};
TEST(Btree, NotAssignableType) {
{
absl::btree_set<NotAssignable> set;
set.emplace(1);
set.emplace_hint(set.end(), 2);
set.insert(NotAssignable(3));
set.insert(set.end(), NotAssignable(4));
EXPECT_THAT(set, ElementsAre(1, 2, 3, 4));
set.erase(set.begin());
EXPECT_THAT(set, ElementsAre(2, 3, 4));
}
{
absl::btree_multiset<NotAssignable> set;
set.emplace(1);
set.emplace_hint(set.end(), 2);
set.insert(NotAssignable(2));
set.insert(set.end(), NotAssignable(3));
EXPECT_THAT(set, ElementsAre(1, 2, 2, 3));
set.erase(set.begin());
EXPECT_THAT(set, ElementsAre(2, 2, 3));
}
{
absl::btree_map<NotAssignable, int> map;
map.emplace(NotAssignable(1), 1);
map.emplace_hint(map.end(), NotAssignable(2), 2);
map.insert({NotAssignable(3), 3});
map.insert(map.end(), {NotAssignable(4), 4});
EXPECT_THAT(map,
ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), Pair(4, 4)));
map.erase(map.begin());
EXPECT_THAT(map, ElementsAre(Pair(2, 2), Pair(3, 3), Pair(4, 4)));
}
{
absl::btree_multimap<NotAssignable, int> map;
map.emplace(NotAssignable(1), 1);
map.emplace_hint(map.end(), NotAssignable(2), 2);
map.insert({NotAssignable(2), 3});
map.insert(map.end(), {NotAssignable(3), 3});
EXPECT_THAT(map,
ElementsAre(Pair(1, 1), Pair(2, 2), Pair(2, 3), Pair(3, 3)));
map.erase(map.begin());
EXPECT_THAT(map, ElementsAre(Pair(2, 2), Pair(2, 3), Pair(3, 3)));
}
}
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END

@ -462,12 +462,6 @@ struct common_params {
static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) {
slot_policy::transfer(alloc, new_slot, old_slot);
}
static void swap(Alloc *alloc, slot_type *a, slot_type *b) {
slot_policy::swap(alloc, a, b);
}
static void move(Alloc *alloc, slot_type *src, slot_type *dest) {
slot_policy::move(alloc, src, dest);
}
};
// An adapter class that converts a lower-bound compare into an upper-bound
@ -2300,23 +2294,29 @@ auto btree<P>::operator=(btree &&other) noexcept -> btree & {
template <typename P>
auto btree<P>::erase(iterator iter) -> iterator {
bool internal_delete = false;
if (iter.node_->is_internal()) {
// Deletion of a value on an internal node. First, move the largest value
// from our left child here, then delete that position (in remove_values()
// below). We can get to the largest value from our left child by
// decrementing iter.
iter.node_->value_destroy(iter.position_, mutable_allocator());
iter.update_generation();
const bool internal_delete = iter.node_->is_internal();
if (internal_delete) {
// Deletion of a value on an internal node. First, transfer the largest
// value from our left child here, then erase/rebalance from that position.
// We can get to the largest value from our left child by decrementing iter.
iterator internal_iter(iter);
--iter;
assert(iter.node_->is_leaf());
params_type::move(mutable_allocator(), iter.node_->slot(iter.position_),
internal_iter.node_->slot(internal_iter.position_));
internal_delete = true;
}
// Delete the key from the leaf.
iter.node_->remove_values(iter.position_, /*to_erase=*/1,
mutable_allocator());
internal_iter.node_->transfer(internal_iter.position_, iter.position_,
iter.node_, mutable_allocator());
} else {
// Shift values after erased position in leaf. In the internal case, we
// don't need to do this because the leaf position is the end of the node.
const field_type transfer_from = iter.position_ + 1;
const field_type num_to_transfer = iter.node_->finish() - transfer_from;
iter.node_->transfer_n(num_to_transfer, iter.position_, transfer_from,
iter.node_, mutable_allocator());
}
// Update node finish and container size.
iter.node_->set_finish(iter.node_->finish() - 1);
--size_;
// We want to return the next value after the one we just erased. If we

@ -433,33 +433,6 @@ struct map_slot_policy {
}
destroy(alloc, old_slot);
}
template <class Allocator>
static void swap(Allocator* alloc, slot_type* a, slot_type* b) {
if (kMutableKeys::value) {
using std::swap;
swap(a->mutable_value, b->mutable_value);
} else {
value_type tmp = std::move(a->value);
absl::allocator_traits<Allocator>::destroy(*alloc, &a->value);
absl::allocator_traits<Allocator>::construct(*alloc, &a->value,
std::move(b->value));
absl::allocator_traits<Allocator>::destroy(*alloc, &b->value);
absl::allocator_traits<Allocator>::construct(*alloc, &b->value,
std::move(tmp));
}
}
template <class Allocator>
static void move(Allocator* alloc, slot_type* src, slot_type* dest) {
if (kMutableKeys::value) {
dest->mutable_value = std::move(src->mutable_value);
} else {
absl::allocator_traits<Allocator>::destroy(*alloc, &dest->value);
absl::allocator_traits<Allocator>::construct(*alloc, &dest->value,
std::move(src->value));
}
}
};
} // namespace container_internal

Loading…
Cancel
Save