]>
Commit | Line | Data |
---|---|---|
9f95a23c | 1 | // Copyright 2018 The Abseil Authors. |
7c673cae FG |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | // you may not use this file except in compliance with the License. | |
5 | // You may obtain a copy of the License at | |
6 | // | |
9f95a23c | 7 | // https://www.apache.org/licenses/LICENSE-2.0 |
7c673cae FG |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software | |
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | // See the License for the specific language governing permissions and | |
13 | // limitations under the License. | |
14 | ||
9f95a23c | 15 | #pragma once |
7c673cae | 16 | |
9f95a23c TL |
17 | #include <algorithm> |
18 | #include <initializer_list> | |
19 | #include <iterator> | |
20 | #include <type_traits> | |
7c673cae FG |
21 | #include <utility> |
22 | ||
23 | #include "btree.h" | |
24 | ||
9f95a23c | 25 | namespace btree::internal { |
7c673cae | 26 | |
9f95a23c | 27 | // A common base class for btree_set, btree_map, btree_multiset, and |
7c673cae FG |
28 | // btree_multimap. |
29 | template <typename Tree> | |
30 | class btree_container { | |
9f95a23c | 31 | using params_type = typename Tree::params_type; |
7c673cae | 32 | |
9f95a23c TL |
33 | protected: |
34 | // Alias used for heterogeneous lookup functions. | |
35 | // `key_arg<K>` evaluates to `K` when the functors are transparent and to | |
36 | // `key_type` otherwise. It permits template argument deduction on `K` for the | |
37 | // transparent case. | |
38 | template <class Compare> | |
39 | using is_transparent_t = typename Compare::is_transparent; | |
40 | template <class K> | |
41 | using key_arg = | |
42 | std::conditional_t< | |
43 | std::experimental::is_detected_v<is_transparent_t, typename Tree::key_compare>, | |
44 | K, | |
45 | typename Tree::key_type>; | |
7c673cae FG |
46 | |
47 | public: | |
9f95a23c TL |
48 | using key_type = typename Tree::key_type; |
49 | using value_type = typename Tree::value_type; | |
50 | using size_type = typename Tree::size_type; | |
51 | using difference_type = typename Tree::difference_type; | |
52 | using key_compare = typename Tree::key_compare; | |
53 | using value_compare = typename Tree::value_compare; | |
54 | using allocator_type = typename Tree::allocator_type; | |
55 | using reference = typename Tree::reference; | |
56 | using const_reference = typename Tree::const_reference; | |
57 | using pointer = typename Tree::pointer; | |
58 | using const_pointer = typename Tree::const_pointer; | |
59 | using iterator = typename Tree::iterator; | |
60 | using const_iterator = typename Tree::const_iterator; | |
61 | using reverse_iterator = typename Tree::reverse_iterator; | |
62 | using const_reverse_iterator = typename Tree::const_reverse_iterator; | |
63 | ||
64 | // Constructors/assignments. | |
65 | btree_container() : tree_(key_compare(), allocator_type()) {} | |
66 | explicit btree_container(const key_compare &comp, | |
67 | const allocator_type &alloc = allocator_type()) | |
68 | : tree_(comp, alloc) {} | |
69 | btree_container(const btree_container &x) = default; | |
70 | btree_container(btree_container &&x) noexcept = default; | |
71 | btree_container &operator=(const btree_container &x) = default; | |
72 | btree_container &operator=(btree_container &&x) noexcept( | |
73 | std::is_nothrow_move_assignable<Tree>::value) = default; | |
7c673cae FG |
74 | |
75 | // Iterator routines. | |
76 | iterator begin() { return tree_.begin(); } | |
77 | const_iterator begin() const { return tree_.begin(); } | |
9f95a23c | 78 | const_iterator cbegin() const { return tree_.begin(); } |
7c673cae FG |
79 | iterator end() { return tree_.end(); } |
80 | const_iterator end() const { return tree_.end(); } | |
9f95a23c | 81 | const_iterator cend() const { return tree_.end(); } |
7c673cae FG |
82 | reverse_iterator rbegin() { return tree_.rbegin(); } |
83 | const_reverse_iterator rbegin() const { return tree_.rbegin(); } | |
9f95a23c | 84 | const_reverse_iterator crbegin() const { return tree_.rbegin(); } |
7c673cae FG |
85 | reverse_iterator rend() { return tree_.rend(); } |
86 | const_reverse_iterator rend() const { return tree_.rend(); } | |
9f95a23c | 87 | const_reverse_iterator crend() const { return tree_.rend(); } |
7c673cae FG |
88 | |
89 | // Lookup routines. | |
9f95a23c TL |
90 | template <typename K = key_type> |
91 | iterator find(const key_arg<K> &key) { | |
92 | return tree_.find(key); | |
93 | } | |
94 | template <typename K = key_type> | |
95 | const_iterator find(const key_arg<K> &key) const { | |
96 | return tree_.find(key); | |
97 | } | |
98 | template <typename K = key_type> | |
99 | bool contains(const key_arg<K> &key) const { | |
100 | return find(key) != end(); | |
101 | } | |
102 | template <typename K = key_type> | |
103 | iterator lower_bound(const key_arg<K> &key) { | |
7c673cae FG |
104 | return tree_.lower_bound(key); |
105 | } | |
9f95a23c TL |
106 | template <typename K = key_type> |
107 | const_iterator lower_bound(const key_arg<K> &key) const { | |
7c673cae FG |
108 | return tree_.lower_bound(key); |
109 | } | |
9f95a23c TL |
110 | template <typename K = key_type> |
111 | iterator upper_bound(const key_arg<K> &key) { | |
7c673cae FG |
112 | return tree_.upper_bound(key); |
113 | } | |
9f95a23c TL |
114 | template <typename K = key_type> |
115 | const_iterator upper_bound(const key_arg<K> &key) const { | |
7c673cae FG |
116 | return tree_.upper_bound(key); |
117 | } | |
9f95a23c TL |
118 | template <typename K = key_type> |
119 | std::pair<iterator, iterator> equal_range(const key_arg<K> &key) { | |
7c673cae FG |
120 | return tree_.equal_range(key); |
121 | } | |
9f95a23c TL |
122 | template <typename K = key_type> |
123 | std::pair<const_iterator, const_iterator> equal_range( | |
124 | const key_arg<K> &key) const { | |
7c673cae FG |
125 | return tree_.equal_range(key); |
126 | } | |
127 | ||
9f95a23c TL |
128 | // Deletion routines. Note that there is also a deletion routine that is |
129 | // specific to btree_set_container/btree_multiset_container. | |
130 | ||
131 | // Erase the specified iterator from the btree. The iterator must be valid | |
132 | // (i.e. not equal to end()). Return an iterator pointing to the node after | |
133 | // the one that was erased (or end() if none exists). | |
134 | iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); } | |
135 | iterator erase(iterator iter) { return tree_.erase(iter); } | |
136 | iterator erase(const_iterator first, const_iterator last) { | |
137 | return tree_.erase(iterator(first), iterator(last)).second; | |
7c673cae FG |
138 | } |
139 | ||
9f95a23c TL |
140 | public: |
141 | // Utility routines. | |
142 | void clear() { tree_.clear(); } | |
143 | void swap(btree_container &x) { tree_.swap(x.tree_); } | |
144 | void verify() const { tree_.verify(); } | |
145 | ||
7c673cae FG |
146 | // Size routines. |
147 | size_type size() const { return tree_.size(); } | |
148 | size_type max_size() const { return tree_.max_size(); } | |
149 | bool empty() const { return tree_.empty(); } | |
9f95a23c TL |
150 | |
151 | friend bool operator==(const btree_container &x, const btree_container &y) { | |
152 | if (x.size() != y.size()) return false; | |
153 | return std::equal(x.begin(), x.end(), y.begin()); | |
7c673cae FG |
154 | } |
155 | ||
9f95a23c TL |
156 | friend bool operator!=(const btree_container &x, const btree_container &y) { |
157 | return !(x == y); | |
7c673cae FG |
158 | } |
159 | ||
9f95a23c TL |
160 | friend bool operator<(const btree_container &x, const btree_container &y) { |
161 | return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); | |
162 | } | |
163 | ||
164 | friend bool operator>(const btree_container &x, const btree_container &y) { | |
165 | return y < x; | |
166 | } | |
167 | ||
168 | friend bool operator<=(const btree_container &x, const btree_container &y) { | |
169 | return !(y < x); | |
170 | } | |
171 | ||
172 | friend bool operator>=(const btree_container &x, const btree_container &y) { | |
173 | return !(x < y); | |
174 | } | |
175 | ||
176 | // The allocator used by the btree. | |
177 | allocator_type get_allocator() const { return tree_.get_allocator(); } | |
178 | ||
179 | // The key comparator used by the btree. | |
180 | key_compare key_comp() const { return tree_.key_comp(); } | |
181 | value_compare value_comp() const { return tree_.value_comp(); } | |
7c673cae FG |
182 | |
183 | protected: | |
184 | Tree tree_; | |
185 | }; | |
186 | ||
9f95a23c | 187 | // A common base class for btree_set and btree_map. |
7c673cae | 188 | template <typename Tree> |
9f95a23c TL |
189 | class btree_set_container : public btree_container<Tree> { |
190 | using super_type = btree_container<Tree>; | |
191 | using params_type = typename Tree::params_type; | |
192 | using init_type = typename params_type::init_type; | |
193 | using is_key_compare_to = typename params_type::is_key_compare_to; | |
194 | friend class BtreeNodePeer; | |
7c673cae | 195 | |
9f95a23c TL |
196 | protected: |
197 | template <class K> | |
198 | using key_arg = typename super_type::template key_arg<K>; | |
7c673cae FG |
199 | |
200 | public: | |
9f95a23c TL |
201 | using key_type = typename Tree::key_type; |
202 | using value_type = typename Tree::value_type; | |
203 | using size_type = typename Tree::size_type; | |
204 | using key_compare = typename Tree::key_compare; | |
205 | using allocator_type = typename Tree::allocator_type; | |
206 | using iterator = typename Tree::iterator; | |
207 | using const_iterator = typename Tree::const_iterator; | |
208 | ||
209 | // Inherit constructors. | |
210 | using super_type::super_type; | |
211 | btree_set_container() {} | |
7c673cae FG |
212 | |
213 | // Range constructor. | |
214 | template <class InputIterator> | |
9f95a23c TL |
215 | btree_set_container(InputIterator b, InputIterator e, |
216 | const key_compare &comp = key_compare(), | |
217 | const allocator_type &alloc = allocator_type()) | |
7c673cae FG |
218 | : super_type(comp, alloc) { |
219 | insert(b, e); | |
220 | } | |
221 | ||
9f95a23c TL |
222 | // Initializer list constructor. |
223 | btree_set_container(std::initializer_list<init_type> init, | |
224 | const key_compare &comp = key_compare(), | |
225 | const allocator_type &alloc = allocator_type()) | |
226 | : btree_set_container(init.begin(), init.end(), comp, alloc) {} | |
227 | ||
7c673cae | 228 | // Lookup routines. |
9f95a23c TL |
229 | template <typename K = key_type> |
230 | size_type count(const key_arg<K> &key) const { | |
7c673cae FG |
231 | return this->tree_.count_unique(key); |
232 | } | |
233 | ||
234 | // Insertion routines. | |
9f95a23c TL |
235 | std::pair<iterator, bool> insert(const value_type &x) { |
236 | return this->tree_.insert_unique(params_type::key(x), x); | |
237 | } | |
238 | std::pair<iterator, bool> insert(value_type &&x) { | |
239 | return this->tree_.insert_unique(params_type::key(x), std::move(x)); | |
240 | } | |
241 | template <typename... Args> | |
242 | std::pair<iterator, bool> emplace(Args &&... args) { | |
243 | init_type v(std::forward<Args>(args)...); | |
244 | return this->tree_.insert_unique(params_type::key(v), std::move(v)); | |
245 | } | |
246 | iterator insert(const_iterator position, const value_type &x) { | |
247 | return this->tree_ | |
248 | .insert_hint_unique(iterator(position), params_type::key(x), x) | |
249 | .first; | |
250 | } | |
251 | iterator insert(const_iterator position, value_type &&x) { | |
252 | return this->tree_ | |
253 | .insert_hint_unique(iterator(position), params_type::key(x), | |
254 | std::move(x)) | |
255 | .first; | |
256 | } | |
257 | template <typename... Args> | |
258 | iterator emplace_hint(const_iterator position, Args &&... args) { | |
259 | init_type v(std::forward<Args>(args)...); | |
260 | return this->tree_ | |
261 | .insert_hint_unique(iterator(position), params_type::key(v), | |
262 | std::move(v)) | |
263 | .first; | |
7c673cae FG |
264 | } |
265 | template <typename InputIterator> | |
266 | void insert(InputIterator b, InputIterator e) { | |
9f95a23c TL |
267 | this->tree_.insert_iterator_unique(b, e); |
268 | } | |
269 | void insert(std::initializer_list<init_type> init) { | |
270 | this->tree_.insert_iterator_unique(init.begin(), init.end()); | |
7c673cae | 271 | } |
7c673cae | 272 | // Deletion routines. |
9f95a23c TL |
273 | template <typename K = key_type> |
274 | size_type erase(const key_arg<K> &key) { | |
7c673cae FG |
275 | return this->tree_.erase_unique(key); |
276 | } | |
9f95a23c TL |
277 | using super_type::erase; |
278 | ||
279 | // Merge routines. | |
280 | // Moves elements from `src` into `this`. If the element already exists in | |
281 | // `this`, it is left unmodified in `src`. | |
282 | template < | |
283 | typename T, | |
284 | typename std::enable_if_t< | |
285 | std::conjunction_v< | |
286 | std::is_same<value_type, typename T::value_type>, | |
287 | std::is_same<allocator_type, typename T::allocator_type>, | |
288 | std::is_same<typename params_type::is_map_container, | |
289 | typename T::params_type::is_map_container>>, | |
290 | int> = 0> | |
291 | void merge(btree_container<T> &src) { // NOLINT | |
292 | for (auto src_it = src.begin(); src_it != src.end();) { | |
293 | if (insert(std::move(*src_it)).second) { | |
294 | src_it = src.erase(src_it); | |
295 | } else { | |
296 | ++src_it; | |
297 | } | |
298 | } | |
7c673cae | 299 | } |
9f95a23c TL |
300 | |
301 | template < | |
302 | typename T, | |
303 | typename std::enable_if_t< | |
304 | std::conjunction_v< | |
305 | std::is_same<value_type, typename T::value_type>, | |
306 | std::is_same<allocator_type, typename T::allocator_type>, | |
307 | std::is_same<typename params_type::is_map_container, | |
308 | typename T::params_type::is_map_container>>, | |
309 | int> = 0> | |
310 | void merge(btree_container<T> &&src) { | |
311 | merge(src); | |
7c673cae FG |
312 | } |
313 | }; | |
314 | ||
315 | // A common base class for btree_map and safe_btree_map. | |
9f95a23c | 316 | // Base class for btree_map. |
7c673cae | 317 | template <typename Tree> |
9f95a23c TL |
318 | class btree_map_container : public btree_set_container<Tree> { |
319 | using super_type = btree_set_container<Tree>; | |
320 | using params_type = typename Tree::params_type; | |
7c673cae | 321 | |
9f95a23c TL |
322 | protected: |
323 | template <class K> | |
324 | using key_arg = typename super_type::template key_arg<K>; | |
7c673cae FG |
325 | |
326 | public: | |
9f95a23c TL |
327 | using key_type = typename Tree::key_type; |
328 | using mapped_type = typename params_type::mapped_type; | |
329 | using value_type = typename Tree::value_type; | |
330 | using key_compare = typename Tree::key_compare; | |
331 | using allocator_type = typename Tree::allocator_type; | |
332 | using iterator = typename Tree::iterator; | |
333 | using const_iterator = typename Tree::const_iterator; | |
334 | ||
335 | // Inherit constructors. | |
336 | using super_type::super_type; | |
337 | btree_map_container() {} | |
7c673cae FG |
338 | |
339 | // Insertion routines. | |
9f95a23c TL |
340 | template <typename... Args> |
341 | std::pair<iterator, bool> try_emplace(const key_type &k, Args &&... args) { | |
342 | return this->tree_.insert_unique( | |
343 | k, std::piecewise_construct, std::forward_as_tuple(k), | |
344 | std::forward_as_tuple(std::forward<Args>(args)...)); | |
345 | } | |
346 | template <typename... Args> | |
347 | std::pair<iterator, bool> try_emplace(key_type &&k, Args &&... args) { | |
348 | // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` | |
349 | // and then using `k` unsequenced. This is safe because the move is into a | |
350 | // forwarding reference and insert_unique guarantees that `key` is never | |
351 | // referenced after consuming `args`. | |
352 | const key_type& key_ref = k; | |
353 | return this->tree_.insert_unique( | |
354 | key_ref, std::piecewise_construct, std::forward_as_tuple(std::move(k)), | |
355 | std::forward_as_tuple(std::forward<Args>(args)...)); | |
356 | } | |
357 | template <typename... Args> | |
358 | iterator try_emplace(const_iterator hint, const key_type &k, | |
359 | Args &&... args) { | |
360 | return this->tree_ | |
361 | .insert_hint_unique(iterator(hint), k, std::piecewise_construct, | |
362 | std::forward_as_tuple(k), | |
363 | std::forward_as_tuple(std::forward<Args>(args)...)) | |
364 | .first; | |
365 | } | |
366 | template <typename... Args> | |
367 | iterator try_emplace(const_iterator hint, key_type &&k, Args &&... args) { | |
368 | // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` | |
369 | // and then using `k` unsequenced. This is safe because the move is into a | |
370 | // forwarding reference and insert_hint_unique guarantees that `key` is | |
371 | // never referenced after consuming `args`. | |
372 | const key_type& key_ref = k; | |
373 | return this->tree_ | |
374 | .insert_hint_unique(iterator(hint), key_ref, std::piecewise_construct, | |
375 | std::forward_as_tuple(std::move(k)), | |
376 | std::forward_as_tuple(std::forward<Args>(args)...)) | |
377 | .first; | |
378 | } | |
379 | mapped_type &operator[](const key_type &k) { | |
380 | return try_emplace(k).first->second; | |
381 | } | |
382 | mapped_type &operator[](key_type &&k) { | |
383 | return try_emplace(std::move(k)).first->second; | |
384 | } | |
385 | ||
386 | template <typename K = key_type> | |
387 | mapped_type &at(const key_arg<K> &key) { | |
388 | auto it = this->find(key); | |
389 | if (it == this->end()) | |
390 | throw std::out_of_range("btree_map::at"); | |
391 | return it->second; | |
392 | } | |
393 | template <typename K = key_type> | |
394 | const mapped_type &at(const key_arg<K> &key) const { | |
395 | auto it = this->find(key); | |
396 | if (it == this->end()) | |
397 | throw std::out_of_range("btree_map::at"); | |
398 | return it->second; | |
7c673cae FG |
399 | } |
400 | }; | |
401 | ||
402 | // A common base class for btree_multiset and btree_multimap. | |
403 | template <typename Tree> | |
9f95a23c TL |
404 | class btree_multiset_container : public btree_container<Tree> { |
405 | using super_type = btree_container<Tree>; | |
406 | using params_type = typename Tree::params_type; | |
407 | using init_type = typename params_type::init_type; | |
408 | using is_key_compare_to = typename params_type::is_key_compare_to; | |
7c673cae | 409 | |
9f95a23c TL |
410 | template <class K> |
411 | using key_arg = typename super_type::template key_arg<K>; | |
7c673cae FG |
412 | |
413 | public: | |
9f95a23c TL |
414 | using key_type = typename Tree::key_type; |
415 | using value_type = typename Tree::value_type; | |
416 | using size_type = typename Tree::size_type; | |
417 | using key_compare = typename Tree::key_compare; | |
418 | using allocator_type = typename Tree::allocator_type; | |
419 | using iterator = typename Tree::iterator; | |
420 | using const_iterator = typename Tree::const_iterator; | |
9f95a23c TL |
421 | |
422 | // Inherit constructors. | |
423 | using super_type::super_type; | |
424 | btree_multiset_container() {} | |
7c673cae FG |
425 | |
426 | // Range constructor. | |
427 | template <class InputIterator> | |
9f95a23c TL |
428 | btree_multiset_container(InputIterator b, InputIterator e, |
429 | const key_compare &comp = key_compare(), | |
430 | const allocator_type &alloc = allocator_type()) | |
7c673cae FG |
431 | : super_type(comp, alloc) { |
432 | insert(b, e); | |
433 | } | |
434 | ||
9f95a23c TL |
435 | // Initializer list constructor. |
436 | btree_multiset_container(std::initializer_list<init_type> init, | |
437 | const key_compare &comp = key_compare(), | |
438 | const allocator_type &alloc = allocator_type()) | |
439 | : btree_multiset_container(init.begin(), init.end(), comp, alloc) {} | |
440 | ||
7c673cae | 441 | // Lookup routines. |
9f95a23c TL |
442 | template <typename K = key_type> |
443 | size_type count(const key_arg<K> &key) const { | |
7c673cae FG |
444 | return this->tree_.count_multi(key); |
445 | } | |
446 | ||
447 | // Insertion routines. | |
9f95a23c TL |
448 | iterator insert(const value_type &x) { return this->tree_.insert_multi(x); } |
449 | iterator insert(value_type &&x) { | |
450 | return this->tree_.insert_multi(std::move(x)); | |
7c673cae | 451 | } |
9f95a23c TL |
452 | iterator insert(const_iterator position, const value_type &x) { |
453 | return this->tree_.insert_hint_multi(iterator(position), x); | |
454 | } | |
455 | iterator insert(const_iterator position, value_type &&x) { | |
456 | return this->tree_.insert_hint_multi(iterator(position), std::move(x)); | |
7c673cae FG |
457 | } |
458 | template <typename InputIterator> | |
459 | void insert(InputIterator b, InputIterator e) { | |
9f95a23c TL |
460 | this->tree_.insert_iterator_multi(b, e); |
461 | } | |
462 | void insert(std::initializer_list<init_type> init) { | |
463 | this->tree_.insert_iterator_multi(init.begin(), init.end()); | |
464 | } | |
465 | template <typename... Args> | |
466 | iterator emplace(Args &&... args) { | |
467 | return this->tree_.insert_multi(init_type(std::forward<Args>(args)...)); | |
468 | } | |
469 | template <typename... Args> | |
470 | iterator emplace_hint(const_iterator position, Args &&... args) { | |
471 | return this->tree_.insert_hint_multi( | |
472 | iterator(position), init_type(std::forward<Args>(args)...)); | |
473 | } | |
7c673cae FG |
474 | |
475 | // Deletion routines. | |
9f95a23c TL |
476 | template <typename K = key_type> |
477 | size_type erase(const key_arg<K> &key) { | |
7c673cae FG |
478 | return this->tree_.erase_multi(key); |
479 | } | |
9f95a23c TL |
480 | using super_type::erase; |
481 | ||
482 | // Merge routines. | |
483 | // Moves all elements from `src` into `this`. | |
484 | template < | |
485 | typename T, | |
486 | typename std::enable_if_t< | |
487 | std::conjunction_v< | |
488 | std::is_same<value_type, typename T::value_type>, | |
489 | std::is_same<allocator_type, typename T::allocator_type>, | |
490 | std::is_same<typename params_type::is_map_container, | |
491 | typename T::params_type::is_map_container>>, | |
492 | int> = 0> | |
493 | void merge(btree_container<T> &src) { // NOLINT | |
494 | insert(std::make_move_iterator(src.begin()), | |
495 | std::make_move_iterator(src.end())); | |
496 | src.clear(); | |
497 | } | |
498 | ||
499 | template < | |
500 | typename T, | |
501 | typename std::enable_if_t< | |
502 | std::conjunction_v< | |
503 | std::is_same<value_type, typename T::value_type>, | |
504 | std::is_same<allocator_type, typename T::allocator_type>, | |
505 | std::is_same<typename params_type::is_map_container, | |
506 | typename T::params_type::is_map_container>>, | |
507 | int> = 0> | |
508 | void merge(btree_container<T> &&src) { | |
509 | merge(src); | |
7c673cae FG |
510 | } |
511 | }; | |
512 | ||
9f95a23c TL |
513 | // A base class for btree_multimap. |
514 | template <typename Tree> | |
515 | class btree_multimap_container : public btree_multiset_container<Tree> { | |
516 | using super_type = btree_multiset_container<Tree>; | |
517 | using params_type = typename Tree::params_type; | |
7c673cae | 518 | |
9f95a23c TL |
519 | public: |
520 | using mapped_type = typename params_type::mapped_type; | |
521 | ||
522 | // Inherit constructors. | |
523 | using super_type::super_type; | |
524 | btree_multimap_container() {} | |
525 | }; | |
526 | } // namespace btree::internal |