Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(641)

Side by Side Diff: base/containers/flat_set.h

Issue 2552343003: First implementation of flat sets (Closed)
Patch Set: Review, round 8. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 // Note: these are general guidelines - important code should be benchmarked.
29 // Try to run your benchmarks on older machines - cost of trade offs between
danakj 2017/02/03 20:30:49 I think this is what our cluter telemetry is for i
dyaroshev 2017/02/04 00:59:15 Done.
30 // different sets might significantly differ.
31 // Prefer base::flat_set for:
danakj 2017/02/03 20:30:49 I think this should say something about size like
dyaroshev 2017/02/04 00:59:15 Ok, I'm done resisting) This is my becnhmark: http
32 // * Sets that are built once (using range constructor). Consider
33 // collecting all data in a vector and then building flat_set using
danakj 2017/02/03 20:30:49 using this constructor
dyaroshev 2017/02/04 00:59:15 I've modified this advice somehow. Looks better?
34 // constructor. TODO(dyaroshev): improve the interface to better support
35 // this pattern (crbug.com/682254).
36 // * Mutating happens in big bulks: use erase(remove) idiom for erasing many
danakj 2017/02/03 20:30:49 Sets where mutating happens.. erase(std::remove(.
dyaroshev 2017/02/04 00:59:15 Did erase(std::remove()) to be consistent with co
37 // elements. Insertion is harder - consider set operations or building a new
38 // vector. Set operations can be slow if one of the sets is considerably
39 // bigger. Also be aware that beating performance of sort + unique
40 // (implementation of flat_set's constructor) is hard, clever merge of many
41 // sets might not win. Generally avoid inserting into flat set without
42 // benchmarks.
43 // * Coping and iterating.
danakj 2017/02/03 20:30:49 Copying?
dyaroshev 2017/02/04 00:59:15 Done.
44 // * Set operations (union/intersect etc).
45 // Prefer std::unordered_set for:
danakj 2017/02/03 20:30:49 I would drop the parts for unordered_set/set. Thos
dyaroshev 2017/02/04 00:59:15 Done.
46 // * Mutating operations on single elements.
47 // * Ordering does not matter. Keep in mind: iteration on an unordered set
48 // does not provide guaranties about order, in depends on the order of
49 // insertions and on the used standard library version - you can not rely on
50 // it.
51 // * Iteration is rare, this is a node base container - coping and iterating is
52 // expensive.
53 // Prefer std::set for:
54 // * Order is important.
55 // * Mutations are somewhat common.
56 // * Iteration/Coping is rare.
57 // * You are on edge. std::set offers better worse case performance than
58 // base::flat_set so potential damage is less.
59 //
60 // TODO(dyaroshev): develop standalone benchmarks to find performance boundaries
61 // for different types of sets crbug.com/682215.
62 //
63 // If you do write a benchmark that significantly depends on using sets please
64 // share your results at:
65 // https://groups.google.com/a/chromium.org/forum/#!searchin/chromium-dev/vector $20based/chromium-dev/4uQMma9vj9w/HaQ-WvMOAwAJ
66 //
67 // Important usability aspects:
68 // * flat_set implements std::set interface from C++11 where possible. It
69 // also has reserve(), capacity() and shrink_to_fit() from std::vector.
70 // * iteration invalidation rules differ:
71 // - all cases of std::vector::iterator invalidation also apply
72 // - we ask (for now) to assume that move operations invalidate iterators.
73 // TODO(dyaroshev): Research the possibility of using a small buffer
74 // optimization crbug.com/682240.
75 // * Constructor sorts elements in a non-stable manner (unlike std::set). So
76 // among equivalent (with respect to provided compare) elements passed to
77 // the constructor it is unspecified with one will end up in the set.
78 // However insert()/emplace() methods are stable with respect to already
79 // inserted elements - an element that is already in the set will not be
80 // replaced.
81 // * allocator support is not implemented.
82 // * insert(first, last) and insert(std::initializer_list) are not
83 // implemented (see Notes section).
84 //
85 // Notes:
86 // Current implementation is based on boost::containers::flat_set,
87 // eastl::vector_set and folly::sorted_vector. All of these implementations do
88 // insert(first, last) as insertion one by one (some implementations with hints
89 // and/or reserve). Boost documentation claims this algorithm to be O(n*log(n))
90 // but it seems to be a quadratic algorithm. For now we do not implement this
91 // method.
92 // TODO(dyaroshev): research an algorithm for range insertion crbug.com/682249.
93
94 template <class Key, class Compare = std::less<Key>>
95 // Meets the requirements of Container, AssociativeContainer,
96 // ReversibleContainer.
97 // Requires: Key is Movable, Compare is a StrictWeakOrdering on Key.
98 class flat_set {
99 private:
100 using underlying_type = std::vector<Key>;
101
102 public:
103 // --------------------------------------------------------------------------
104 // Types.
105 //
106 using key_type = Key;
107 using key_compare = Compare;
108 using value_type = Key;
109 using value_compare = Compare;
110
111 using pointer = typename underlying_type::pointer;
112 using const_pointer = typename underlying_type::const_pointer;
113 using reference = typename underlying_type::reference;
114 using const_reference = typename underlying_type::const_reference;
115 using size_type = typename underlying_type::size_type;
116 using difference_type = typename underlying_type::difference_type;
117 using iterator = typename underlying_type::iterator;
118 using const_iterator = typename underlying_type::const_iterator;
119 using reverse_iterator = typename underlying_type::reverse_iterator;
120 using const_reverse_iterator =
121 typename underlying_type::const_reverse_iterator;
122
123 // --------------------------------------------------------------------------
124 // Lifetime.
125 //
126 // Constructors that take range guarantee O(N * log^2(N)) + O(N) complexity
127 // and take O(N * log(N)) + O(N) if extra memory is available (N is a range
128 // length).
129 //
130 // Assume that move constructors invalidate iterators and references.
131
132 flat_set();
133 explicit flat_set(const Compare& comp);
134
135 template <class InputIterator>
136 flat_set(InputIterator first,
137 InputIterator last,
138 const Compare& comp = Compare());
139
140 flat_set(const flat_set&);
141 flat_set(flat_set&&);
142
143 flat_set(std::initializer_list<value_type> ilist,
144 const Compare& comp = Compare());
145
146 ~flat_set();
147
148 // --------------------------------------------------------------------------
149 // Assignments.
150 //
151 // Assume that move assignment invalidates iterators and references.
152
153 flat_set& operator=(const flat_set&);
154 flat_set& operator=(flat_set&&);
155 flat_set& operator=(std::initializer_list<value_type> ilist);
156
157 // --------------------------------------------------------------------------
158 // Memory management.
159 //
160 // Beware that shrink_to_fit() simply forwards the request to the
161 // underlying_type and its implementation is free to optimize otherwise and
162 // leave capacity() to be greater that its size.
163 //
164 // reserve() and shrink_to_fit() invalidate iterators and references.
165
166 void reserve(size_type new_capacity);
167 size_type capacity() const;
168 void shrink_to_fit();
169
170 // --------------------------------------------------------------------------
171 // Size management.
172 //
173 // clear() leaves the capacity() of the flat_set unchanged.
174
175 void clear();
176
177 size_type size() const;
178 size_type max_size() const;
179 bool empty() const;
180
181 // --------------------------------------------------------------------------
182 // Iterators.
183
184 iterator begin();
185 const_iterator begin() const;
186 const_iterator cbegin() const;
187
188 iterator end();
189 const_iterator end() const;
190 const_iterator cend() const;
191
192 reverse_iterator rbegin();
193 const_reverse_iterator rbegin() const;
194 const_reverse_iterator crbegin() const;
195
196 reverse_iterator rend();
197 const_reverse_iterator rend() const;
198 const_reverse_iterator crend() const;
199
200 // --------------------------------------------------------------------------
201 // Insert operations.
202 //
203 // Assume that every operation invalidates iterators and references.
204 // Insertion of one element can take O(size). See the Notes section in the
205 // class comments on why we do not currently implement range insertion.
206 // Capacity of flat_set grows in an implementation-defined manner.
207
208 std::pair<iterator, bool> insert(const value_type& val);
209 std::pair<iterator, bool> insert(value_type&& val);
210
211 iterator insert(const_iterator position_hint, const value_type& x);
212 iterator insert(const_iterator position_hint, value_type&& x);
213
214 template <class... Args>
215 std::pair<iterator, bool> emplace(Args&&... args);
216
217 template <class... Args>
218 iterator emplace_hint(const_iterator position_hint, Args&&... args);
219
220 // --------------------------------------------------------------------------
221 // Erase operations.
222 //
223 // Assume that every operation invalidates iterators and references.
224 //
225 // erase(position), erase(first, last) can take O(size).
226 // erase(key) may take O(size) + O(log(size)).
227 //
228 // Prefer the erase(std::remove(), end()) idiom for deleting multiple
229 // elements.
230
231 iterator erase(const_iterator position);
232 iterator erase(const_iterator first, const_iterator last);
233 size_type erase(const key_type& key);
234
235 // --------------------------------------------------------------------------
236 // Comparators.
237
238 key_compare key_comp() const;
239 value_compare value_comp() const;
240
241 // --------------------------------------------------------------------------
242 // Search operations.
243 //
244 // Search operations have O(log(size)) complexity.
245
246 size_type count(const key_type& key) const;
247
248 iterator find(const key_type& key);
249 const_iterator find(const key_type& key) const;
250
251 std::pair<iterator, iterator> equal_range(const key_type& ket);
252 std::pair<const_iterator, const_iterator> equal_range(
253 const key_type& key) const;
254
255 iterator lower_bound(const key_type& key);
256 const_iterator lower_bound(const key_type& key) const;
257
258 iterator upper_bound(const key_type& key);
259 const_iterator upper_bound(const key_type& key) const;
260
261 // --------------------------------------------------------------------------
262 // General operations.
263 //
264 // Assume that swap invalidates iterators and references.
265 //
266 // As with std::set, equality and ordering operations for the whole flat_set
267 // are equivalent to using equal() and lexicographical_compare() on the key
268 // types, rather than using element-wise key_comp() as e.g. lower_bound()
269 // does. Implementation note: currently we use operator==() and operator<() on
270 // std::vector, because they have the same contract we need, so we use them
271 // directly for brevity and in case it is more optimal than calling equal()
272 // and lexicograhpical_compare(). If the underlying container type is changed,
273 // this code may need to be modified.
274
275 void swap(flat_set& other);
276
277 friend bool operator==(const flat_set& lhs, const flat_set& rhs) {
278 return lhs.impl_.body_ == rhs.impl_.body_;
279 }
280
281 friend bool operator!=(const flat_set& lhs, const flat_set& rhs) {
282 return !operator==(lhs, rhs);
283 }
284
285 friend bool operator<(const flat_set& lhs, const flat_set& rhs) {
286 return lhs.impl_.body_ < rhs.impl_.body_;
287 }
288
289 friend bool operator>(const flat_set& lhs, const flat_set& rhs) {
290 return rhs < lhs;
291 }
292
293 friend bool operator>=(const flat_set& lhs, const flat_set& rhs) {
294 return !(lhs < rhs);
295 }
296
297 friend bool operator<=(const flat_set& lhs, const flat_set& rhs) {
298 return !(lhs > rhs);
299 }
300
301 friend void swap(flat_set& lhs, flat_set& rhs) { lhs.swap(rhs); }
302
303 private:
304 const flat_set& as_const() { return *this; }
305
306 iterator const_cast_it(const_iterator c_it) {
307 auto distance = std::distance(cbegin(), c_it);
308 return std::next(begin(), distance);
309 }
310
311 void sort_and_unique() {
312 // std::set sorts elements preserving stability because it doesn't have any
313 // performance wins in not doing that. We do so we use an unstable sort.
danakj 2017/02/03 20:30:49 We do, so
dyaroshev 2017/02/04 00:59:15 Done.
314 std::sort(begin(), end(), value_comp());
315 erase(std::unique(begin(), end(),
316 [this](const value_type& lhs, const value_type& rhs) {
317 // lhs is already <= rhs due to sort, therefore
318 // !(lhs < rhs) <=> lhs == rhs.
319 return !value_comp()(lhs, rhs);
320 }),
321 end());
322 }
323
324 // To support comparators that may not be possible to default-construct, we
325 // have to store an instance of Compare. Using this to store all internal
326 // state of flat_set and using private inheritance to store compare lets us
327 // take advantage of an empty base class optimization to avoid extra space in
328 // the common case when Compare has no state.
329 struct Impl : private Compare {
330 Impl() = default;
331
332 template <class Cmp, class... Body>
333 explicit Impl(Cmp&& compare_arg, Body&&... underlying_type_args)
334 : Compare(std::forward<Cmp>(compare_arg)),
335 body_(std::forward<Body>(underlying_type_args)...) {}
336
337 Compare compare() const { return *this; }
338
339 underlying_type body_;
340 } impl_;
341 };
342
343 // ----------------------------------------------------------------------------
344 // Lifetime.
345
346 template <class Key, class Compare>
347 flat_set<Key, Compare>::flat_set() = default;
348
349 template <class Key, class Compare>
350 flat_set<Key, Compare>::flat_set(const Compare& comp) : impl_(comp) {}
351
352 template <class Key, class Compare>
353 template <class InputIterator>
354 flat_set<Key, Compare>::flat_set(InputIterator first,
355 InputIterator last,
356 const Compare& comp)
357 : impl_(comp, first, last) {
358 sort_and_unique();
359 }
360
361 template <class Key, class Compare>
362 flat_set<Key, Compare>::flat_set(const flat_set&) = default;
363
364 template <class Key, class Compare>
365 flat_set<Key, Compare>::flat_set(flat_set&&) = default;
366
367 template <class Key, class Compare>
368 flat_set<Key, Compare>::flat_set(std::initializer_list<value_type> ilist,
369 const Compare& comp)
370 : flat_set(std::begin(ilist), std::end(ilist), comp) {}
371
372 template <class Key, class Compare>
373 flat_set<Key, Compare>::~flat_set() = default;
374
375 // ----------------------------------------------------------------------------
376 // Assignments.
377
378 template <class Key, class Compare>
379 auto flat_set<Key, Compare>::operator=(const flat_set&) -> flat_set& = default;
380
381 template <class Key, class Compare>
382 auto flat_set<Key, Compare>::operator=(flat_set &&) -> flat_set& = default;
383
384 template <class Key, class Compare>
385 auto flat_set<Key, Compare>::operator=(std::initializer_list<value_type> ilist)
386 -> flat_set& {
387 impl_.body_ = ilist;
388 sort_and_unique();
389 return *this;
390 }
391
392 // ----------------------------------------------------------------------------
393 // Memory management.
394
395 template <class Key, class Compare>
396 void flat_set<Key, Compare>::reserve(size_type new_capacity) {
397 impl_.body_.reserve(new_capacity);
398 }
399
400 template <class Key, class Compare>
401 auto flat_set<Key, Compare>::capacity() const -> size_type {
402 return impl_.body_.capacity();
403 }
404
405 template <class Key, class Compare>
406 void flat_set<Key, Compare>::shrink_to_fit() {
407 impl_.body_.shrink_to_fit();
408 }
409
410 // ----------------------------------------------------------------------------
411 // Size management.
412
413 template <class Key, class Compare>
414 void flat_set<Key, Compare>::clear() {
415 impl_.body_.clear();
416 }
417
418 template <class Key, class Compare>
419 auto flat_set<Key, Compare>::size() const -> size_type {
420 return impl_.body_.size();
421 }
422
423 template <class Key, class Compare>
424 auto flat_set<Key, Compare>::max_size() const -> size_type {
425 return impl_.body_.max_size();
426 }
427
428 template <class Key, class Compare>
429 bool flat_set<Key, Compare>::empty() const {
430 return impl_.body_.empty();
431 }
432
433 // ----------------------------------------------------------------------------
434 // Iterators.
435
436 template <class Key, class Compare>
437 auto flat_set<Key, Compare>::begin() -> iterator {
438 return impl_.body_.begin();
439 }
440
441 template <class Key, class Compare>
442 auto flat_set<Key, Compare>::begin() const -> const_iterator {
443 return impl_.body_.begin();
444 }
445
446 template <class Key, class Compare>
447 auto flat_set<Key, Compare>::cbegin() const -> const_iterator {
448 return impl_.body_.cbegin();
449 }
450
451 template <class Key, class Compare>
452 auto flat_set<Key, Compare>::end() -> iterator {
453 return impl_.body_.end();
454 }
455
456 template <class Key, class Compare>
457 auto flat_set<Key, Compare>::end() const -> const_iterator {
458 return impl_.body_.end();
459 }
460
461 template <class Key, class Compare>
462 auto flat_set<Key, Compare>::cend() const -> const_iterator {
463 return impl_.body_.cend();
464 }
465
466 template <class Key, class Compare>
467 auto flat_set<Key, Compare>::rbegin() -> reverse_iterator {
468 return impl_.body_.rbegin();
469 }
470
471 template <class Key, class Compare>
472 auto flat_set<Key, Compare>::rbegin() const -> const_reverse_iterator {
473 return impl_.body_.rbegin();
474 }
475
476 template <class Key, class Compare>
477 auto flat_set<Key, Compare>::crbegin() const -> const_reverse_iterator {
478 return impl_.body_.crbegin();
479 }
480
481 template <class Key, class Compare>
482 auto flat_set<Key, Compare>::rend() -> reverse_iterator {
483 return impl_.body_.rend();
484 }
485
486 template <class Key, class Compare>
487 auto flat_set<Key, Compare>::rend() const -> const_reverse_iterator {
488 return impl_.body_.rend();
489 }
490
491 template <class Key, class Compare>
492 auto flat_set<Key, Compare>::crend() const -> const_reverse_iterator {
493 return impl_.body_.crend();
494 }
495
496 // ----------------------------------------------------------------------------
497 // Insert operations.
498 //
499 // Currently we use position_hint the same way as eastl or boost:
500 // https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set. h#L493
501 //
502 // We duplicate code between copy and move version so that we can avoid
503 // creating a temporary value.
504
505 template <class Key, class Compare>
506 auto flat_set<Key, Compare>::insert(const value_type& val)
507 -> std::pair<iterator, bool> {
508 auto position = lower_bound(val);
509
510 if (position == end() || value_comp()(val, *position))
511 return {impl_.body_.insert(position, val), true};
512
513 return {position, false};
514 }
515
516 template <class Key, class Compare>
517 auto flat_set<Key, Compare>::insert(value_type&& val)
518 -> std::pair<iterator, bool> {
519 auto position = lower_bound(val);
520
521 if (position == end() || value_comp()(val, *position))
522 return {impl_.body_.insert(position, std::move(val)), true};
523
524 return {position, false};
525 }
526
527 template <class Key, class Compare>
528 auto flat_set<Key, Compare>::insert(const_iterator position_hint,
529 const value_type& val) -> iterator {
530 if (position_hint == end() || value_comp()(val, *position_hint)) {
531 if (position_hint == begin() || value_comp()(*(position_hint - 1), val))
532 // We have to cast away const because of crbug.com/677044.
533 return impl_.body_.insert(const_cast_it(position_hint), val);
534 }
535 return insert(val).first;
536 }
537
538 template <class Key, class Compare>
539 auto flat_set<Key, Compare>::insert(const_iterator position_hint,
540 value_type&& val) -> iterator {
541 if (position_hint == end() || value_comp()(val, *position_hint)) {
542 if (position_hint == begin() || value_comp()(*(position_hint - 1), val))
543 // We have to cast away const because of crbug.com/677044.
544 return impl_.body_.insert(const_cast_it(position_hint), std::move(val));
545 }
546 return insert(std::move(val)).first;
547 }
548
549 template <class Key, class Compare>
550 template <class... Args>
551 auto flat_set<Key, Compare>::emplace(Args&&... args)
552 -> std::pair<iterator, bool> {
553 return insert(value_type(std::forward<Args>(args)...));
554 }
555
556 template <class Key, class Compare>
557 template <class... Args>
558 auto flat_set<Key, Compare>::emplace_hint(const_iterator position_hint,
559 Args&&... args) -> iterator {
560 return insert(position_hint, value_type(std::forward<Args>(args)...));
561 }
562
563 // ----------------------------------------------------------------------------
564 // Erase operations.
565
566 template <class Key, class Compare>
567 auto flat_set<Key, Compare>::erase(const_iterator position) -> iterator {
568 // We have to cast away const because of crbug.com/677044.
569 return impl_.body_.erase(const_cast_it(position));
570 }
571
572 template <class Key, class Compare>
573 auto flat_set<Key, Compare>::erase(const key_type& val) -> size_type {
574 auto eq_range = equal_range(val);
575 auto res = std::distance(eq_range.first, eq_range.second);
576 // We have to cast away const because of crbug.com/677044.
577 erase(const_cast_it(eq_range.first), const_cast_it(eq_range.second));
578 return res;
579 }
580
581 template <class Key, class Compare>
582 auto flat_set<Key, Compare>::erase(const_iterator first, const_iterator last)
583 -> iterator {
584 // We have to cast away const because of crbug.com/677044.
585 return impl_.body_.erase(const_cast_it(first), const_cast_it(last));
586 }
587
588 // ----------------------------------------------------------------------------
589 // Comparators.
590
591 template <class Key, class Compare>
592 auto flat_set<Key, Compare>::key_comp() const -> key_compare {
593 return impl_.compare();
594 }
595
596 template <class Key, class Compare>
597 auto flat_set<Key, Compare>::value_comp() const -> value_compare {
598 return impl_.compare();
599 }
600
601 // ----------------------------------------------------------------------------
602 // Search operations.
603
604 template <class Key, class Compare>
605 auto flat_set<Key, Compare>::count(const key_type& key) const -> size_type {
606 auto eq_range = equal_range(key);
607 return std::distance(eq_range.first, eq_range.second);
608 }
609
610 template <class Key, class Compare>
611 auto flat_set<Key, Compare>::find(const key_type& key) -> iterator {
612 return const_cast_it(as_const().find(key));
613 }
614
615 template <class Key, class Compare>
616 auto flat_set<Key, Compare>::find(const key_type& key) const -> const_iterator {
617 auto eq_range = equal_range(key);
618 return (eq_range.first == eq_range.second) ? end() : eq_range.first;
619 }
620
621 template <class Key, class Compare>
622 auto flat_set<Key, Compare>::equal_range(const key_type& key)
623 -> std::pair<iterator, iterator> {
624 auto res = as_const().equal_range(key);
625 return {const_cast_it(res.first), const_cast_it(res.second)};
626 }
627
628 template <class Key, class Compare>
629 auto flat_set<Key, Compare>::equal_range(const key_type& key) const
630 -> std::pair<const_iterator, const_iterator> {
631 auto lower = lower_bound(key);
632
633 if (lower == end() || key_comp()(key, *lower))
634 return {lower, lower};
635
636 return {lower, std::next(lower)};
637 }
638
639 template <class Key, class Compare>
640 auto flat_set<Key, Compare>::lower_bound(const key_type& key) -> iterator {
641 return const_cast_it(as_const().lower_bound(key));
642 }
643
644 template <class Key, class Compare>
645 auto flat_set<Key, Compare>::lower_bound(const key_type& key) const
646 -> const_iterator {
647 return std::lower_bound(begin(), end(), key, key_comp());
648 }
649
650 template <class Key, class Compare>
651 auto flat_set<Key, Compare>::upper_bound(const key_type& key) -> iterator {
652 return const_cast_it(as_const().upper_bound(key));
653 }
654
655 template <class Key, class Compare>
656 auto flat_set<Key, Compare>::upper_bound(const key_type& key) const
657 -> const_iterator {
658 return std::upper_bound(begin(), end(), key, key_comp());
659 }
660
661 // ----------------------------------------------------------------------------
662 // General operations.
663
664 template <class Key, class Compare>
665 void flat_set<Key, Compare>::swap(flat_set& other) {
666 std::swap(impl_, other.impl_);
667 }
668
669 } // namespace base
670
671 #endif // BASE_CONTAINERS_FLAT_SET_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698