aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/testsuite/23_containers/multiset/modifiers/114401.cc
blob: 630e18e287c64e664a39f46204591a5829bba05e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// { dg-do run { target c++17 } }

// PR libstdc++/114401 allocator destructor omitted when reinserting node_handle

#include <set>
#include <memory>
#include <testsuite_hooks.h>

template<typename T>
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<int>(identity)) { }

  template<typename U>
    Alloc(const Alloc<U> a) : id(a.id) { }

  T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
  void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); }

  template<typename U>
    friend bool
    operator==(const Alloc& a, const Alloc<U>& a2)
    { return a.id == a2.id; }

  template<typename U>
    friend bool
    operator!=(const Alloc& a, const Alloc<U>& a2)
    { return !(a == a2); }

  std::shared_ptr<int> id;
};

using test_type = std::multiset<int, std::less<int>, Alloc<int>>;

void
test_node_ops()
{
  test_type s1({1,3,5}, Alloc<int>(1));
  test_type s2({2,4,6,8}, Alloc<int>(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<int> 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();
}