OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef BASE_CONTAINERS_FLAT_SET_H_ | |
6 #define BASE_CONTAINERS_FLAT_SET_H_ | |
7 | |
8 #include <algorithm> | |
9 #include <functional> | |
10 #include <utility> | |
11 #include <vector> | |
12 | |
13 namespace base { | |
14 | |
15 // Overview: | |
16 // This file implements flat_set container. It is an alternative to standard | |
17 // sorted containers that stores it's elements in contiguous memory (current | |
18 // version uses sorted std::vector). | |
19 // Discussion that preceded introduction of this container can be found here: | |
20 // https://groups.google.com/a/chromium.org/forum/#!searchin/chromium-dev/vector $20based/chromium-dev/4uQMma9vj9w/HaQ-WvMOAwAJ | |
21 // | |
22 // Motivation: | |
23 // Contiguous memory is very beneficial to iteration and copy speed at the cost | |
24 // of worse algorithmic complexity of insertion/erasure operations. They can | |
25 // be very fast for set operations and for small number of elements. | |
26 // | |
27 // Usage guidance: | |
28 // Unless you have good benchmarks prefer base::flat_set over std::set for sets | |
29 // that are built once and then practically never mutate. | |
30 // TODO(dyaroshev): develop standalone benchmarks to find performance boundaries | |
31 // for different types performance of different sets crbug.com/682215. | |
dyaroshev
2017/01/30 14:10:58
for different types of sets.
| |
32 // | |
33 // Important usability aspects: | |
34 // * flat_set implements std::set interface from C++11 where possible. It | |
35 // also has reserve(), capacity() and shrink_to_fit() from std::vector. | |
36 // * iteration invalidation rules differ: | |
37 // - all cases of std::vector::iterator invalidation also apply | |
38 // - we ask (for now) to assume that move operations invalidate iterators. | |
39 // TODO(dyaroshev): Research the possibility of using a small buffer | |
40 // optimization crbug.com/682240. | |
41 // * allocator support is not implemented. | |
42 // * insert(first, last) and insert(std::initializer_list) are not | |
43 // implemented (see Notes section). | |
44 // | |
45 // Notes: | |
46 // Current implementation is based on boost::containers::flat_set, | |
47 // eastl::vector_set and folly::sorted_vector. All of these implementations do | |
48 // insert(first, last) as insertion one by one (some implementations with hints | |
49 // and/or reserve). Boost documentation claims this algorithm to be O(n*log(n)) | |
50 // but it seems to be a quadratic algorithm. For now we do not implement this | |
51 // method. | |
52 // TODO(dyaroshev): research an algorithm for range insertion crbug.com/682249. | |
53 | |
54 template <class Key, class Compare = std::less<Key>> | |
55 // Meets the requirements of Container, AssociativeContainer, | |
56 // ReversibleContainer. | |
danakj
2017/01/30 20:24:31
just wrap lines normally
dyaroshev
2017/01/31 23:08:25
Done.
| |
57 // Requires: | |
58 // Key is Movable | |
59 // Compare is a StrictWeakOrdering on Key | |
60 class flat_set { | |
61 private: | |
62 using underlying_type = std::vector<Key>; | |
63 | |
64 public: | |
65 // -------------------------------------------------------------------------- | |
66 // Types. | |
67 // | |
68 using key_type = Key; | |
69 using key_compare = Compare; | |
70 using value_type = Key; | |
71 using value_compare = Compare; | |
72 | |
73 using pointer = typename underlying_type::pointer; | |
74 using const_pointer = typename underlying_type::const_pointer; | |
75 using reference = typename underlying_type::reference; | |
76 using const_reference = typename underlying_type::const_reference; | |
77 using size_type = typename underlying_type::size_type; | |
78 using difference_type = typename underlying_type::difference_type; | |
79 using iterator = typename underlying_type::iterator; | |
80 using const_iterator = typename underlying_type::const_iterator; | |
81 using reverse_iterator = typename underlying_type::reverse_iterator; | |
82 using const_reverse_iterator = | |
83 typename underlying_type::const_reverse_iterator; | |
84 | |
85 // -------------------------------------------------------------------------- | |
86 // Lifetime. | |
87 // | |
88 // Constructors that take range guarantee O(N * log^2(N)) + O(N) complexity | |
89 // and take O(N * log(N)) + O(N) if extra memory is available (N is a range | |
90 // length). | |
91 // | |
92 // Assume that move constructors invalidate iterators and references. | |
93 | |
94 flat_set(); | |
95 explicit flat_set(const Compare& comp); | |
96 | |
97 template <class InputIterator> | |
98 flat_set(InputIterator first, | |
99 InputIterator last, | |
100 const Compare& comp = Compare()); | |
101 | |
102 flat_set(const flat_set&); | |
103 flat_set(flat_set&&); | |
104 | |
105 flat_set(std::initializer_list<value_type> ilist, | |
106 const Compare& comp = Compare()); | |
107 | |
108 ~flat_set(); | |
109 | |
110 // -------------------------------------------------------------------------- | |
111 // Assignments. | |
112 // | |
113 // Assume that move assignment invalidates iterators and references. | |
114 | |
115 flat_set& operator=(const flat_set&); | |
116 flat_set& operator=(flat_set&&); | |
117 flat_set& operator=(std::initializer_list<value_type> ilist); | |
118 | |
119 // -------------------------------------------------------------------------- | |
120 // Memory management. | |
121 // | |
122 // Beware that shrink_to_fit() simply forwards the request to the | |
123 // underlying_type and its implementation is free to optimize otherwise and | |
124 // leave capacity() to be greater that its size. | |
125 // | |
126 // reserve() and shrink_to_fit() invalidate iterators and references. | |
127 | |
128 void reserve(size_type new_capacity); | |
129 size_type capacity() const; | |
130 void shrink_to_fit(); | |
131 | |
132 // -------------------------------------------------------------------------- | |
133 // Size management. | |
134 // | |
135 // clear() leaves the capacity() of the flat_set unchanged. | |
136 | |
137 void clear(); | |
138 | |
139 size_type size() const; | |
140 size_type max_size() const; | |
141 bool empty() const; | |
142 | |
143 // -------------------------------------------------------------------------- | |
144 // Iterators. | |
145 | |
146 iterator begin(); | |
147 const_iterator begin() const; | |
148 const_iterator cbegin() const; | |
149 | |
150 iterator end(); | |
151 const_iterator end() const; | |
152 const_iterator cend() const; | |
153 | |
154 reverse_iterator rbegin(); | |
155 const_reverse_iterator rbegin() const; | |
156 const_reverse_iterator crbegin() const; | |
157 | |
158 reverse_iterator rend(); | |
159 const_reverse_iterator rend() const; | |
160 const_reverse_iterator crend() const; | |
161 | |
162 // -------------------------------------------------------------------------- | |
163 // Insert operations. | |
164 // | |
165 // Assume that every operation invalidates iterators and references. | |
166 // Insertion of one element can take O(size). See the Notes section in the | |
167 // class comments on why we do not currently implement range insertion. | |
168 // Capacity of flat_set grows in an implementation-defined manner. | |
169 | |
170 std::pair<iterator, bool> insert(const value_type& val); | |
171 std::pair<iterator, bool> insert(value_type&& val); | |
172 | |
173 iterator insert(const_iterator position_hint, const value_type& x); | |
174 iterator insert(const_iterator position_hint, value_type&& x); | |
175 | |
176 template <class... Args> | |
177 std::pair<iterator, bool> emplace(Args&&... args); | |
178 | |
179 template <class... Args> | |
180 iterator emplace_hint(const_iterator position_hint, Args&&... args); | |
181 | |
182 // -------------------------------------------------------------------------- | |
183 // Erase operations. | |
184 // | |
185 // Assume that every operation invalidates iterators and references. | |
186 // | |
187 // erase(position), erase(first, last) can take O(size). | |
188 // erase(key) may take O(size) + O(log(size)). | |
189 // | |
190 // Prefer the erase(std::remove(), end()) idiom for deleting multiple | |
191 // elements. | |
192 | |
193 iterator erase(const_iterator position); | |
194 iterator erase(const_iterator first, const_iterator last); | |
195 size_type erase(const key_type& key); | |
196 | |
197 // -------------------------------------------------------------------------- | |
198 // Comparators. | |
199 | |
200 key_compare key_comp() const; | |
201 value_compare value_comp() const; | |
202 | |
203 // -------------------------------------------------------------------------- | |
204 // Search operations. | |
205 // | |
206 // Search operations have O(log(size)) complexity. | |
207 | |
208 size_type count(const key_type& key) const; | |
209 | |
210 iterator find(const key_type& key); | |
211 const_iterator find(const key_type& key) const; | |
212 | |
213 std::pair<iterator, iterator> equal_range(const key_type& ket); | |
214 std::pair<const_iterator, const_iterator> equal_range( | |
215 const key_type& key) const; | |
216 | |
217 iterator lower_bound(const key_type& key); | |
218 const_iterator lower_bound(const key_type& key) const; | |
219 | |
220 iterator upper_bound(const key_type& key); | |
221 const_iterator upper_bound(const key_type& key) const; | |
222 | |
223 // -------------------------------------------------------------------------- | |
224 // General operations. | |
225 // | |
226 // Assume that swap invalidates iterators and references. | |
227 // | |
228 // As with std::set, equality and ordering operations for the whole flat_set | |
229 // are equivalent to using equal() and lexicographical_compare() on the key | |
230 // types, rather than using element-wise key_comp() as e.g. lower_bound() | |
231 // does. Implementation note: currently we use operator==() and operator<() on | |
232 // std::vector, because they have the same contract we need, so we use them | |
233 // directly for brevity and in case it is more optimal than calling equal() | |
234 // and lexicograhpical_compare(). If the underlying container type is changed, | |
235 // this code may need to be modified. | |
236 | |
237 void swap(flat_set& other); | |
238 | |
239 friend bool operator==(const flat_set& lhs, const flat_set& rhs) { | |
240 return lhs.impl_.body_ == rhs.impl_.body_; | |
241 } | |
242 | |
243 friend bool operator!=(const flat_set& lhs, const flat_set& rhs) { | |
244 return !operator==(lhs, rhs); | |
245 } | |
246 | |
247 friend bool operator<(const flat_set& lhs, const flat_set& rhs) { | |
248 return lhs.impl_.body_ < rhs.impl_.body_; | |
249 } | |
250 | |
251 friend bool operator>(const flat_set& lhs, const flat_set& rhs) { | |
252 return rhs < lhs; | |
253 } | |
254 | |
255 friend bool operator>=(const flat_set& lhs, const flat_set& rhs) { | |
256 return !(lhs < rhs); | |
257 } | |
258 | |
259 friend bool operator<=(const flat_set& lhs, const flat_set& rhs) { | |
260 return !(lhs > rhs); | |
261 } | |
262 | |
263 friend void swap(flat_set& lhs, flat_set& rhs) { lhs.swap(rhs); } | |
264 | |
265 private: | |
266 const flat_set& as_const() { return *this; } | |
267 | |
268 iterator const_cast_it(const_iterator c_it) { | |
269 auto distance = std::distance(cbegin(), c_it); | |
270 return std::next(begin(), distance); | |
271 } | |
272 | |
273 void sort_and_unique() { | |
274 // std::set sorts elements preserving stability. If we were to use unstable | |
275 // sort this could introduce hard to diagnose bugs. | |
276 std::stable_sort(begin(), end(), value_comp()); | |
dyaroshev
2017/01/30 00:07:13
Stability bugs me, I would like your opinion on th
danakj
2017/01/30 20:28:38
I don't think we need to preserve ordering. I woul
dyaroshev
2017/01/31 23:08:25
Done.
| |
277 erase(std::unique(begin(), end(), | |
278 [this](const value_type& lhs, const value_type& rhs) { | |
279 // lhs is already <= rhs due to sort, therefore !(lhs < | |
280 // rhs) <=> | |
danakj
2017/01/30 20:24:31
dont line wrap here?
dyaroshev
2017/01/31 23:08:25
Done.
| |
281 // lhs == rhs. | |
282 return !value_comp()(lhs, rhs); | |
283 }), | |
284 end()); | |
285 } | |
286 | |
287 // To support comparators that may not be possible to default-construct, we | |
288 // have to store an instance of Compare. Putting all internal state of | |
danakj
2017/01/30 20:24:31
"Putting all internal state" => "Using this to sto
dyaroshev
2017/01/31 23:08:25
Done.
| |
289 // flat_set and using private inheritance to store compare lets us take | |
290 // advantage of an empty base class optimization to avoid extra space in the | |
291 // common case when Compare has no state. | |
292 struct Impl : Compare { | |
danakj
2017/01/30 20:24:31
make private explicit
dyaroshev
2017/01/31 23:08:25
Done. Had to do a few modifications - it wasn't p
| |
293 Impl() = default; | |
294 | |
295 template <class Cmp, class... Body> | |
296 explicit Impl(Cmp&& compare_arg, Body&&... underlying_type_args) | |
297 : Compare(std::forward<Cmp>(compare_arg)), | |
298 body_(std::forward<Body>(underlying_type_args)...) {} | |
299 | |
300 underlying_type body_; | |
301 } impl_; | |
302 }; | |
303 | |
304 // ---------------------------------------------------------------------------- | |
305 // Lifetime. | |
306 | |
307 template <class Key, class Compare> | |
308 flat_set<Key, Compare>::flat_set() = default; | |
309 | |
310 template <class Key, class Compare> | |
311 flat_set<Key, Compare>::flat_set(const Compare& comp) : impl_(comp) {} | |
312 | |
313 template <class Key, class Compare> | |
314 template <class InputIterator> | |
315 flat_set<Key, Compare>::flat_set(InputIterator first, | |
316 InputIterator last, | |
317 const Compare& comp) | |
318 : impl_(comp, first, last) { | |
319 sort_and_unique(); | |
320 } | |
321 | |
322 template <class Key, class Compare> | |
323 flat_set<Key, Compare>::flat_set(const flat_set&) = default; | |
324 | |
325 template <class Key, class Compare> | |
326 flat_set<Key, Compare>::flat_set(flat_set&&) = default; | |
327 | |
328 template <class Key, class Compare> | |
329 flat_set<Key, Compare>::flat_set(std::initializer_list<value_type> ilist, | |
330 const Compare& comp) | |
331 : flat_set(std::begin(ilist), std::end(ilist), comp) {} | |
332 | |
333 template <class Key, class Compare> | |
334 flat_set<Key, Compare>::~flat_set() = default; | |
335 | |
336 // ---------------------------------------------------------------------------- | |
337 // Assignments. | |
338 | |
339 template <class Key, class Compare> | |
340 auto flat_set<Key, Compare>::operator=(const flat_set&) -> flat_set& = default; | |
341 | |
342 template <class Key, class Compare> | |
343 auto flat_set<Key, Compare>::operator=(flat_set &&) -> flat_set& = default; | |
danakj
2017/01/30 20:24:31
no space before &&
dyaroshev
2017/01/31 23:08:25
Done.
danakj
2017/02/03 20:30:49
Looks to be missed. Is clang-format doing this?
dyaroshev
2017/02/04 00:59:15
Yeah, sorry. Want me to disable it?
Peter Kasting
2017/02/04 01:03:35
That sounds like a clang-fmt bug (it sounds like i
dcheng
2017/02/04 05:57:18
I filed https://llvm.org/bugs/show_bug.cgi?id=3186
| |
344 | |
345 template <class Key, class Compare> | |
346 auto flat_set<Key, Compare>::operator=(std::initializer_list<value_type> ilist) | |
347 -> flat_set& { | |
348 impl_.body_ = ilist; | |
349 sort_and_unique(); | |
350 return *this; | |
351 } | |
352 | |
353 // ---------------------------------------------------------------------------- | |
354 // Memory management. | |
355 | |
356 template <class Key, class Compare> | |
357 void flat_set<Key, Compare>::reserve(size_type new_capacity) { | |
358 impl_.body_.reserve(new_capacity); | |
359 } | |
360 | |
361 template <class Key, class Compare> | |
362 auto flat_set<Key, Compare>::capacity() const -> size_type { | |
363 return impl_.body_.capacity(); | |
364 } | |
365 | |
366 template <class Key, class Compare> | |
367 void flat_set<Key, Compare>::shrink_to_fit() { | |
368 impl_.body_.shrink_to_fit(); | |
369 } | |
370 | |
371 // ---------------------------------------------------------------------------- | |
372 // Size management. | |
373 | |
374 template <class Key, class Compare> | |
375 void flat_set<Key, Compare>::clear() { | |
376 impl_.body_.clear(); | |
377 } | |
378 | |
379 template <class Key, class Compare> | |
380 auto flat_set<Key, Compare>::size() const -> size_type { | |
381 return impl_.body_.size(); | |
382 } | |
383 | |
384 template <class Key, class Compare> | |
385 auto flat_set<Key, Compare>::max_size() const -> size_type { | |
386 return impl_.body_.max_size(); | |
387 } | |
388 | |
389 template <class Key, class Compare> | |
390 bool flat_set<Key, Compare>::empty() const { | |
391 return impl_.body_.empty(); | |
392 } | |
393 | |
394 // ---------------------------------------------------------------------------- | |
395 // Iterators. | |
396 | |
397 template <class Key, class Compare> | |
398 auto flat_set<Key, Compare>::begin() -> iterator { | |
399 return impl_.body_.begin(); | |
400 } | |
401 | |
402 template <class Key, class Compare> | |
403 auto flat_set<Key, Compare>::begin() const -> const_iterator { | |
404 return impl_.body_.begin(); | |
405 } | |
406 | |
407 template <class Key, class Compare> | |
408 auto flat_set<Key, Compare>::cbegin() const -> const_iterator { | |
409 return impl_.body_.cbegin(); | |
410 } | |
411 | |
412 template <class Key, class Compare> | |
413 auto flat_set<Key, Compare>::end() -> iterator { | |
414 return impl_.body_.end(); | |
415 } | |
416 | |
417 template <class Key, class Compare> | |
418 auto flat_set<Key, Compare>::end() const -> const_iterator { | |
419 return impl_.body_.end(); | |
420 } | |
421 | |
422 template <class Key, class Compare> | |
423 auto flat_set<Key, Compare>::cend() const -> const_iterator { | |
424 return impl_.body_.cend(); | |
425 } | |
426 | |
427 template <class Key, class Compare> | |
428 auto flat_set<Key, Compare>::rbegin() -> reverse_iterator { | |
429 return impl_.body_.rbegin(); | |
430 } | |
431 | |
432 template <class Key, class Compare> | |
433 auto flat_set<Key, Compare>::rbegin() const -> const_reverse_iterator { | |
434 return impl_.body_.rbegin(); | |
435 } | |
436 | |
437 template <class Key, class Compare> | |
438 auto flat_set<Key, Compare>::crbegin() const -> const_reverse_iterator { | |
439 return impl_.body_.crbegin(); | |
440 } | |
441 | |
442 template <class Key, class Compare> | |
443 auto flat_set<Key, Compare>::rend() -> reverse_iterator { | |
444 return impl_.body_.rend(); | |
445 } | |
446 | |
447 template <class Key, class Compare> | |
448 auto flat_set<Key, Compare>::rend() const -> const_reverse_iterator { | |
449 return impl_.body_.rend(); | |
450 } | |
451 | |
452 template <class Key, class Compare> | |
453 auto flat_set<Key, Compare>::crend() const -> const_reverse_iterator { | |
454 return impl_.body_.crend(); | |
455 } | |
456 | |
457 // ---------------------------------------------------------------------------- | |
458 // Insert operations. | |
459 // | |
460 // Currently we use hint the same way as eastl or boost: | |
danakj
2017/01/30 20:24:31
we use the position hint
dyaroshev
2017/01/31 23:08:25
Done.
| |
461 // https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set. h#L493 | |
462 // | |
463 // We duplicate code between copy and move version so that we can avoid | |
464 // creating a temporary value. | |
465 | |
466 template <class Key, class Compare> | |
467 auto flat_set<Key, Compare>::insert(const value_type& val) | |
468 -> std::pair<iterator, bool> { | |
469 auto position = lower_bound(val); | |
470 | |
471 if (position == end() || value_comp()(val, *position)) | |
472 return {impl_.body_.insert(position, val), true}; | |
473 | |
474 return {position, false}; | |
475 } | |
476 | |
477 template <class Key, class Compare> | |
478 auto flat_set<Key, Compare>::insert(value_type&& val) | |
479 -> std::pair<iterator, bool> { | |
480 auto position = lower_bound(val); | |
481 | |
482 if (position == end() || value_comp()(val, *position)) | |
483 return {impl_.body_.insert(position, std::move(val)), true}; | |
484 | |
485 return {position, false}; | |
486 } | |
487 | |
488 template <class Key, class Compare> | |
489 auto flat_set<Key, Compare>::insert(const_iterator position_hint, | |
490 const value_type& val) -> iterator { | |
491 if (position_hint == end() || value_comp()(val, *position_hint)) { | |
492 if (position_hint == begin() || value_comp()(*(position_hint - 1), val)) | |
493 // We have to cast away const because of crbug.com/677044. | |
494 return impl_.body_.insert(const_cast_it(position_hint), val); | |
495 } | |
496 return insert(val).first; | |
497 } | |
498 | |
499 template <class Key, class Compare> | |
500 auto flat_set<Key, Compare>::insert(const_iterator position_hint, | |
501 value_type&& val) -> iterator { | |
502 if (position_hint == end() || value_comp()(val, *position_hint)) { | |
503 if (position_hint == begin() || value_comp()(*(position_hint - 1), val)) | |
504 // We have to cast away const because of crbug.com/677044. | |
505 return impl_.body_.insert(const_cast_it(position_hint), std::move(val)); | |
506 } | |
507 return insert(std::move(val)).first; | |
508 } | |
509 | |
510 template <class Key, class Compare> | |
511 template <class... Args> | |
512 auto flat_set<Key, Compare>::emplace(Args&&... args) | |
513 -> std::pair<iterator, bool> { | |
514 return insert(value_type(std::forward<Args>(args)...)); | |
515 } | |
516 | |
517 template <class Key, class Compare> | |
518 template <class... Args> | |
519 auto flat_set<Key, Compare>::emplace_hint(const_iterator position_hint, | |
520 Args&&... args) -> iterator { | |
521 return insert(position_hint, value_type(std::forward<Args>(args)...)); | |
522 } | |
523 | |
524 // ---------------------------------------------------------------------------- | |
525 // Erase operations. | |
526 | |
527 template <class Key, class Compare> | |
528 auto flat_set<Key, Compare>::erase(const_iterator position) -> iterator { | |
529 // We have to cast away const because of crbug.com/677044. | |
530 return impl_.body_.erase(const_cast_it(position)); | |
531 } | |
532 | |
533 template <class Key, class Compare> | |
534 auto flat_set<Key, Compare>::erase(const key_type& val) -> size_type { | |
535 auto eq_range = equal_range(val); | |
536 auto res = std::distance(eq_range.first, eq_range.second); | |
537 // We have to cast away const because of crbug.com/677044. | |
538 erase(const_cast_it(eq_range.first), const_cast_it(eq_range.second)); | |
539 return res; | |
540 } | |
541 | |
542 template <class Key, class Compare> | |
543 auto flat_set<Key, Compare>::erase(const_iterator first, const_iterator last) | |
544 -> iterator { | |
545 // We have to cast away const because of crbug.com/677044. | |
546 return impl_.body_.erase(const_cast_it(first), const_cast_it(last)); | |
547 } | |
548 | |
549 // ---------------------------------------------------------------------------- | |
550 // Comparators. | |
551 | |
552 template <class Key, class Compare> | |
553 auto flat_set<Key, Compare>::key_comp() const -> key_compare { | |
554 return key_compare(impl_); | |
555 } | |
556 | |
557 template <class Key, class Compare> | |
558 auto flat_set<Key, Compare>::value_comp() const -> value_compare { | |
559 return value_compare(impl_); | |
560 } | |
561 | |
562 // ---------------------------------------------------------------------------- | |
563 // Search operations. | |
564 | |
565 template <class Key, class Compare> | |
566 auto flat_set<Key, Compare>::count(const key_type& key) const -> size_type { | |
567 auto eq_range = equal_range(key); | |
568 return std::distance(eq_range.first, eq_range.second); | |
569 } | |
570 | |
571 template <class Key, class Compare> | |
572 auto flat_set<Key, Compare>::find(const key_type& key) -> iterator { | |
573 return const_cast_it(as_const().find(key)); | |
574 } | |
575 | |
576 template <class Key, class Compare> | |
577 auto flat_set<Key, Compare>::find(const key_type& key) const -> const_iterator { | |
578 auto eq_range = equal_range(key); | |
579 return (eq_range.first == eq_range.second) ? end() : eq_range.first; | |
580 } | |
581 | |
582 template <class Key, class Compare> | |
583 auto flat_set<Key, Compare>::equal_range(const key_type& key) | |
584 -> std::pair<iterator, iterator> { | |
585 auto res = as_const().equal_range(key); | |
586 return {const_cast_it(res.first), const_cast_it(res.second)}; | |
587 } | |
588 | |
589 template <class Key, class Compare> | |
590 auto flat_set<Key, Compare>::equal_range(const key_type& key) const | |
591 -> std::pair<const_iterator, const_iterator> { | |
592 auto lower = lower_bound(key); | |
593 | |
594 if (lower == end() || key_comp()(key, *lower)) | |
595 return {lower, lower}; | |
596 | |
597 return {lower, std::next(lower)}; | |
598 } | |
599 | |
600 template <class Key, class Compare> | |
601 auto flat_set<Key, Compare>::lower_bound(const key_type& key) -> iterator { | |
602 return const_cast_it(as_const().lower_bound(key)); | |
603 } | |
604 | |
605 template <class Key, class Compare> | |
606 auto flat_set<Key, Compare>::lower_bound(const key_type& key) const | |
607 -> const_iterator { | |
608 return std::lower_bound(begin(), end(), key, key_comp()); | |
609 } | |
610 | |
611 template <class Key, class Compare> | |
612 auto flat_set<Key, Compare>::upper_bound(const key_type& key) -> iterator { | |
613 return const_cast_it(as_const().upper_bound(key)); | |
614 } | |
615 | |
616 template <class Key, class Compare> | |
617 auto flat_set<Key, Compare>::upper_bound(const key_type& key) const | |
618 -> const_iterator { | |
619 return std::upper_bound(begin(), end(), key, key_comp()); | |
620 } | |
621 | |
622 // ---------------------------------------------------------------------------- | |
623 // General operations. | |
624 | |
625 template <class Key, class Compare> | |
626 void flat_set<Key, Compare>::swap(flat_set& other) { | |
627 std::swap(impl_, other.impl_); | |
628 } | |
629 | |
630 } // namespace base | |
631 | |
632 #endif // BASE_CONTAINERS_FLAT_SET_H_ | |
OLD | NEW |