// { dg-do run { target c++17 } } // PR libstdc++/114401 allocator destructor omitted when reinserting node_handle #include #include #include template struct Alloc { using value_type = T; using propagate_on_container_copy_assignment = std::true_type; using propagate_on_container_move_assignment = std::true_type; using propagate_on_container_swap = std::true_type; Alloc(int identity) : id(std::make_shared(identity)) { } template Alloc(const Alloc a) : id(a.id) { } T* allocate(std::size_t n) { return std::allocator().allocate(n); } void deallocate(T* p, std::size_t n) { std::allocator().deallocate(p, n); } template friend bool operator==(const Alloc& a, const Alloc& a2) { return a.id == a2.id; } template friend bool operator!=(const Alloc& a, const Alloc& a2) { return !(a == a2); } std::shared_ptr id; }; using test_type = std::multiset, Alloc>; void test_node_ops() { test_type s1({1,3,5}, Alloc(1)); test_type s2({2,4,6,8}, Alloc(2)); VERIFY( s1.get_allocator() != s2.get_allocator() ); auto node_a = s1.extract(1); VERIFY( ! node_a.empty() ); VERIFY( node_a.get_allocator() == s1.get_allocator() ); node_a = std::move(node_a); // self-move VERIFY( node_a.empty() ); swap(node_a, node_a); // self-swap VERIFY( node_a.empty() ); auto node_b = s2.extract(2); VERIFY( node_b.get_allocator() == s2.get_allocator() ); node_a = std::move(node_b); // empty = !empty VERIFY( node_a.get_allocator() == s2.get_allocator() ); VERIFY( node_b.empty() ); swap(node_a, node_b); // swap(!empty, empty) VERIFY( node_a.empty() ); VERIFY( node_b.get_allocator() == s2.get_allocator() ); swap(node_a, node_b); // swap(empty, !empty) VERIFY( node_a.get_allocator() == s2.get_allocator() ); VERIFY( node_b.empty() ); node_a = s1.extract(3); // !empty = !empty VERIFY( node_a.get_allocator() == s1.get_allocator() ); node_b = s2.extract(0); // empty = empty VERIFY( node_b.empty() ); node_b = s2.extract(6); // empty = !empty VERIFY( node_b.get_allocator() == s2.get_allocator() ); swap(node_a, node_b); // swap(!empty, !empty) VERIFY( node_a.get_allocator() == s2.get_allocator() ); VERIFY( node_b.get_allocator() == s1.get_allocator() ); node_a = {}; node_b = std::move(node_a); // !empty = empty VERIFY( node_a.empty() ); VERIFY( node_b.empty() ); swap(node_a, node_b); // swap(empty, empty) VERIFY( node_a.empty() ); VERIFY( node_b.empty() ); } void test_alloc_lifetime() { Alloc a(1); test_type s({1,2,3}, a); VERIFY( a.id.use_count() == 2 ); // a and the copy in s s.insert(s.extract(1)); VERIFY( a.id.use_count() == 2 ); s.insert(s.begin(), s.extract(2)); VERIFY( a.id.use_count() == 2 ); auto node = s.extract(1); VERIFY( a.id.use_count() == 3 ); node = s.extract(0); VERIFY( a.id.use_count() == 2 ); s.insert(std::move(node)); VERIFY( a.id.use_count() == 2 ); s.merge(test_type(s)); VERIFY( a.id.use_count() == 2 ); s.merge(test_type({4,5,6}, a)); VERIFY( a.id.use_count() == 2 ); } int main() { test_node_ops(); test_alloc_lifetime(); }