2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * This header defines two classes that very nearly model
19 * AssociativeContainer (but not quite). These implement set-like and
20 * map-like behavior on top of a sorted vector, instead of using
21 * rb-trees like std::set and std::map.
23 * This is potentially useful in cases where the number of elements in
24 * the set or map is small, or when you want to avoid using more
25 * memory than necessary and insertions/deletions are much more rare
26 * than lookups (these classes have O(N) insertions/deletions).
28 * In the interest of using these in conditions where the goal is to
29 * minimize memory usage, they support a GrowthPolicy parameter, which
30 * is a class defining a single function called increase_capacity,
31 * which will be called whenever we are about to insert something: you
32 * can then decide to call reserve() based on the current capacity()
33 * and size() of the passed in vector-esque Container type. An
34 * example growth policy that grows one element at a time:
36 * struct OneAtATimePolicy {
37 * template <class Container>
38 * void increase_capacity(Container& c) {
39 * if (c.size() == c.capacity()) {
40 * c.reserve(c.size() + 1);
45 * typedef sorted_vector_set<int,
47 * std::allocator<int>,
51 * Important differences from std::set and std::map:
52 * - insert() and erase() invalidate iterators and references
53 * - insert() and erase() are O(N)
54 * - our iterators model RandomAccessIterator
55 * - sorted_vector_map::value_type is pair<K,V>, not pair<const K,V>.
56 * (This is basically because we want to store the value_type in
57 * std::vector<>, which requires it to be Assignable.)
63 #include <initializer_list>
66 #include <type_traits>
70 #include <boost/operators.hpp>
72 #include <folly/Traits.h>
73 #include <folly/portability/BitsFunctexcept.h>
77 //////////////////////////////////////////////////////////////////////
81 template <typename, typename Compare, typename Key, typename T>
82 struct sorted_vector_enable_if_is_transparent {};
84 template <typename Compare, typename Key, typename T>
85 struct sorted_vector_enable_if_is_transparent<
86 void_t<typename Compare::is_transparent>,
93 // This wrapper goes around a GrowthPolicy and provides iterator
94 // preservation semantics, but only if the growth policy is not the
95 // default (i.e. nothing).
96 template <class Policy>
97 struct growth_policy_wrapper : private Policy {
98 template <class Container, class Iterator>
99 Iterator increase_capacity(Container& c, Iterator desired_insertion) {
100 typedef typename Container::difference_type diff_t;
101 diff_t d = desired_insertion - c.begin();
102 Policy::increase_capacity(c);
103 return c.begin() + d;
107 struct growth_policy_wrapper<void> {
108 template <class Container, class Iterator>
109 Iterator increase_capacity(Container&, Iterator it) {
115 * This helper returns the distance between two iterators if it is
116 * possible to figure it out without messing up the range
117 * (i.e. unless they are InputIterators). Otherwise this returns
120 template <class Iterator>
121 int distance_if_multipass(Iterator first, Iterator last) {
122 typedef typename std::iterator_traits<Iterator>::iterator_category categ;
123 if (std::is_same<categ, std::input_iterator_tag>::value) {
126 return std::distance(first, last);
129 template <class OurContainer, class Vector, class GrowthPolicy>
130 typename OurContainer::iterator insert_with_hint(
131 OurContainer& sorted,
133 typename OurContainer::iterator hint,
134 typename OurContainer::value_type&& value,
136 const typename OurContainer::value_compare& cmp(sorted.value_comp());
137 if (hint == cont.end() || cmp(value, *hint)) {
138 if (hint == cont.begin() || cmp(*(hint - 1), value)) {
139 hint = po.increase_capacity(cont, hint);
140 return cont.insert(hint, std::move(value));
142 return sorted.insert(std::move(value)).first;
146 if (cmp(*hint, value)) {
147 if (hint + 1 == cont.end() || cmp(value, *(hint + 1))) {
148 hint = po.increase_capacity(cont, hint + 1);
149 return cont.insert(hint, std::move(value));
151 return sorted.insert(std::move(value)).first;
155 // Value and *hint did not compare, so they are equal keys.
159 template <class OurContainer, class Vector, class InputIterator>
161 OurContainer& sorted,
164 InputIterator last) {
165 // prevent deref of middle where middle == cont.end()
170 auto const& cmp(sorted.value_comp());
172 int const d = distance_if_multipass(first, last);
174 cont.reserve(cont.size() + d);
176 auto const prev_size = cont.size();
178 std::copy(first, last, std::back_inserter(cont));
179 auto const middle = cont.begin() + prev_size;
180 if (!std::is_sorted(middle, cont.end(), cmp)) {
181 std::sort(middle, cont.end(), cmp);
183 if (middle != cont.begin() && !cmp(*(middle - 1), *middle)) {
184 std::inplace_merge(cont.begin(), middle, cont.end(), cmp);
189 [&](typename OurContainer::value_type const& a,
190 typename OurContainer::value_type const& b) {
191 return !cmp(a, b) && !cmp(b, a);
196 } // namespace detail
198 //////////////////////////////////////////////////////////////////////
201 * A sorted_vector_set is a container similar to std::set<>, but
202 * implemented as as a sorted array with std::vector<>.
204 * @param class T Data type to store
205 * @param class Compare Comparison function that imposes a
206 * strict weak ordering over instances of T
207 * @param class Allocator allocation policy
208 * @param class GrowthPolicy policy object to control growth
210 * @author Aditya Agarwal <aditya@fb.com>
211 * @author Akhil Wable <akhil@fb.com>
212 * @author Jordan DeLong <delong.j@fb.com>
216 class Compare = std::less<T>,
217 class Allocator = std::allocator<T>,
218 class GrowthPolicy = void>
219 class sorted_vector_set
220 : boost::totally_ordered1<
221 sorted_vector_set<T,Compare,Allocator,GrowthPolicy>
222 , detail::growth_policy_wrapper<GrowthPolicy> >
224 typedef std::vector<T,Allocator> ContainerT;
226 detail::growth_policy_wrapper<GrowthPolicy>&
227 get_growth_policy() { return *this; }
229 template <typename K, typename V, typename C = Compare>
230 using if_is_transparent =
231 _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>;
234 typedef T value_type;
236 typedef Compare key_compare;
237 typedef Compare value_compare;
239 typedef typename ContainerT::pointer pointer;
240 typedef typename ContainerT::reference reference;
241 typedef typename ContainerT::const_reference const_reference;
243 * XXX: Our normal iterator ought to also be a constant iterator
244 * (cf. Defect Report 103 for std::set), but this is a bit more of a
247 typedef typename ContainerT::iterator iterator;
248 typedef typename ContainerT::const_iterator const_iterator;
249 typedef typename ContainerT::difference_type difference_type;
250 typedef typename ContainerT::size_type size_type;
251 typedef typename ContainerT::reverse_iterator reverse_iterator;
252 typedef typename ContainerT::const_reverse_iterator const_reverse_iterator;
254 explicit sorted_vector_set(const Compare& comp = Compare(),
255 const Allocator& alloc = Allocator())
259 template <class InputIterator>
260 explicit sorted_vector_set(
263 const Compare& comp = Compare(),
264 const Allocator& alloc = Allocator())
267 // This is linear if [first, last) is already sorted (and if we
268 // can figure out the distance between the two iterators).
272 /* implicit */ sorted_vector_set(
273 std::initializer_list<value_type> list,
274 const Compare& comp = Compare(),
275 const Allocator& alloc = Allocator())
278 insert(list.begin(), list.end());
281 // Construct a sorted_vector_set by stealing the storage of a prefilled
282 // container. The container need not be sorted already. This supports
283 // bulk construction of sorted_vector_set with zero allocations, not counting
284 // those performed by the caller. (The iterator range constructor performs at
285 // least one allocation).
287 // Note that `sorted_vector_set(const ContainerT& container)` is not provided,
288 // since the purpose of this constructor is to avoid an unnecessary copy.
289 explicit sorted_vector_set(
290 ContainerT&& container,
291 const Compare& comp = Compare())
292 : m_(comp, container.get_allocator()) {
293 std::sort(container.begin(), container.end(), value_comp());
294 m_.cont_.swap(container);
297 key_compare key_comp() const { return m_; }
298 value_compare value_comp() const { return m_; }
300 iterator begin() { return m_.cont_.begin(); }
301 iterator end() { return m_.cont_.end(); }
302 const_iterator cbegin() const { return m_.cont_.cbegin(); }
303 const_iterator begin() const { return m_.cont_.begin(); }
304 const_iterator cend() const { return m_.cont_.cend(); }
305 const_iterator end() const { return m_.cont_.end(); }
306 reverse_iterator rbegin() { return m_.cont_.rbegin(); }
307 reverse_iterator rend() { return m_.cont_.rend(); }
308 const_reverse_iterator rbegin() const { return m_.cont_.rbegin(); }
309 const_reverse_iterator rend() const { return m_.cont_.rend(); }
311 void clear() { return m_.cont_.clear(); }
312 size_type size() const { return m_.cont_.size(); }
313 size_type max_size() const { return m_.cont_.max_size(); }
314 bool empty() const { return m_.cont_.empty(); }
315 void reserve(size_type s) { return m_.cont_.reserve(s); }
316 void shrink_to_fit() { m_.cont_.shrink_to_fit(); }
317 size_type capacity() const { return m_.cont_.capacity(); }
319 std::pair<iterator,bool> insert(const value_type& value) {
320 return insert(std::move(value_type(value)));
323 std::pair<iterator,bool> insert(value_type&& value) {
324 iterator it = lower_bound(value);
325 if (it == end() || value_comp()(value, *it)) {
326 it = get_growth_policy().increase_capacity(m_.cont_, it);
327 return std::make_pair(m_.cont_.insert(it, std::move(value)), true);
329 return std::make_pair(it, false);
332 iterator insert(iterator hint, const value_type& value) {
333 return insert(hint, std::move(value_type(value)));
336 iterator insert(iterator hint, value_type&& value) {
337 return detail::insert_with_hint(*this, m_.cont_, hint, std::move(value),
338 get_growth_policy());
341 template <class InputIterator>
342 void insert(InputIterator first, InputIterator last) {
343 detail::bulk_insert(*this, m_.cont_, first, last);
346 size_type erase(const key_type& key) {
347 iterator it = find(key);
355 void erase(iterator it) {
359 void erase(iterator first, iterator last) {
360 m_.cont_.erase(first, last);
363 iterator find(const key_type& key) {
364 return find(*this, key);
367 const_iterator find(const key_type& key) const {
368 return find(*this, key);
371 template <typename K>
372 if_is_transparent<K, iterator> find(const K& key) {
373 return find(*this, key);
376 template <typename K>
377 if_is_transparent<K, const_iterator> find(const K& key) const {
378 return find(*this, key);
381 size_type count(const key_type& key) const {
382 return find(key) == end() ? 0 : 1;
385 template <typename K>
386 if_is_transparent<K, size_type> count(const K& key) const {
387 return find(key) == end() ? 0 : 1;
390 iterator lower_bound(const key_type& key) {
391 return std::lower_bound(begin(), end(), key, key_comp());
394 const_iterator lower_bound(const key_type& key) const {
395 return std::lower_bound(begin(), end(), key, key_comp());
398 template <typename K>
399 if_is_transparent<K, iterator> lower_bound(const K& key) {
400 return std::lower_bound(begin(), end(), key, key_comp());
403 template <typename K>
404 if_is_transparent<K, const_iterator> lower_bound(const K& key) const {
405 return std::lower_bound(begin(), end(), key, key_comp());
408 iterator upper_bound(const key_type& key) {
409 return std::upper_bound(begin(), end(), key, key_comp());
412 const_iterator upper_bound(const key_type& key) const {
413 return std::upper_bound(begin(), end(), key, key_comp());
416 template <typename K>
417 if_is_transparent<K, iterator> upper_bound(const K& key) {
418 return std::upper_bound(begin(), end(), key, key_comp());
421 template <typename K>
422 if_is_transparent<K, const_iterator> upper_bound(const K& key) const {
423 return std::upper_bound(begin(), end(), key, key_comp());
426 std::pair<iterator, iterator> equal_range(const key_type& key) {
427 return std::equal_range(begin(), end(), key, key_comp());
430 std::pair<const_iterator, const_iterator> equal_range(
431 const key_type& key) const {
432 return std::equal_range(begin(), end(), key, key_comp());
435 template <typename K>
436 if_is_transparent<K, std::pair<iterator, iterator>> equal_range(
438 return std::equal_range(begin(), end(), key, key_comp());
441 template <typename K>
442 if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range(
443 const K& key) const {
444 return std::equal_range(begin(), end(), key, key_comp());
447 // Nothrow as long as swap() on the Compare type is nothrow.
448 void swap(sorted_vector_set& o) {
449 using std::swap; // Allow ADL for swap(); fall back to std::swap().
453 m_.cont_.swap(o.m_.cont_);
456 bool operator==(const sorted_vector_set& other) const {
457 return other.m_.cont_ == m_.cont_;
460 bool operator<(const sorted_vector_set& other) const {
461 return m_.cont_ < other.m_.cont_;
466 * This structure derives from the comparison object in order to
467 * make use of the empty base class optimization if our comparison
468 * functor is an empty class (usual case).
470 * Wrapping up this member like this is better than deriving from
471 * the Compare object ourselves (there are some perverse edge cases
472 * involving virtual functions).
474 * More info: http://www.cantrip.org/emptyopt.html
476 struct EBO : Compare {
477 explicit EBO(const Compare& c, const Allocator& alloc)
484 template <typename Self>
485 using self_iterator_t = _t<
486 std::conditional<std::is_const<Self>::value, const_iterator, iterator>>;
488 template <typename Self, typename K>
489 static self_iterator_t<Self> find(Self& self, K const& key) {
490 auto end = self.end();
491 auto it = self.lower_bound(key);
492 if (it == end || !self.key_comp()(key, *it)) {
499 // Swap function that can be found using ADL.
500 template <class T, class C, class A, class G>
501 inline void swap(sorted_vector_set<T,C,A,G>& a,
502 sorted_vector_set<T,C,A,G>& b) {
506 //////////////////////////////////////////////////////////////////////
509 * A sorted_vector_map is similar to a sorted_vector_set but stores
510 * <key,value> pairs instead of single elements.
512 * @param class Key Key type
513 * @param class Value Value type
514 * @param class Compare Function that can compare key types and impose
515 * a strict weak ordering over them.
516 * @param class Allocator allocation policy
517 * @param class GrowthPolicy policy object to control growth
519 * @author Aditya Agarwal <aditya@fb.com>
520 * @author Akhil Wable <akhil@fb.com>
521 * @author Jordan DeLong <delong.j@fb.com>
526 class Compare = std::less<Key>,
527 class Allocator = std::allocator<std::pair<Key, Value>>,
528 class GrowthPolicy = void>
529 class sorted_vector_map
530 : boost::totally_ordered1<
531 sorted_vector_map<Key,Value,Compare,Allocator,GrowthPolicy>
532 , detail::growth_policy_wrapper<GrowthPolicy> >
534 typedef std::vector<std::pair<Key,Value>,Allocator> ContainerT;
536 detail::growth_policy_wrapper<GrowthPolicy>&
537 get_growth_policy() { return *this; }
539 template <typename K, typename V, typename C = Compare>
540 using if_is_transparent =
541 _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>;
544 typedef Key key_type;
545 typedef Value mapped_type;
546 typedef std::pair<key_type,mapped_type> value_type;
547 typedef Compare key_compare;
549 struct value_compare : private Compare {
550 bool operator()(const value_type& a, const value_type& b) const {
551 return Compare::operator()(a.first, b.first);
555 friend class sorted_vector_map;
556 explicit value_compare(const Compare& c) : Compare(c) {}
559 typedef typename ContainerT::pointer pointer;
560 typedef typename ContainerT::reference reference;
561 typedef typename ContainerT::const_reference const_reference;
562 typedef typename ContainerT::iterator iterator;
563 typedef typename ContainerT::const_iterator const_iterator;
564 typedef typename ContainerT::difference_type difference_type;
565 typedef typename ContainerT::size_type size_type;
566 typedef typename ContainerT::reverse_iterator reverse_iterator;
567 typedef typename ContainerT::const_reverse_iterator const_reverse_iterator;
569 explicit sorted_vector_map(const Compare& comp = Compare(),
570 const Allocator& alloc = Allocator())
571 : m_(value_compare(comp), alloc)
574 template <class InputIterator>
575 explicit sorted_vector_map(
578 const Compare& comp = Compare(),
579 const Allocator& alloc = Allocator())
580 : m_(value_compare(comp), alloc)
585 explicit sorted_vector_map(
586 std::initializer_list<value_type> list,
587 const Compare& comp = Compare(),
588 const Allocator& alloc = Allocator())
589 : m_(value_compare(comp), alloc)
591 insert(list.begin(), list.end());
594 // Construct a sorted_vector_map by stealing the storage of a prefilled
595 // container. The container need not be sorted already. This supports
596 // bulk construction of sorted_vector_map with zero allocations, not counting
597 // those performed by the caller. (The iterator range constructor performs at
598 // least one allocation).
600 // Note that `sorted_vector_map(const ContainerT& container)` is not provided,
601 // since the purpose of this constructor is to avoid an unnecessary copy.
602 explicit sorted_vector_map(
603 ContainerT&& container,
604 const Compare& comp = Compare())
605 : m_(value_compare(comp), container.get_allocator()) {
606 std::sort(container.begin(), container.end(), value_comp());
607 m_.cont_.swap(container);
610 key_compare key_comp() const { return m_; }
611 value_compare value_comp() const { return m_; }
613 iterator begin() { return m_.cont_.begin(); }
614 iterator end() { return m_.cont_.end(); }
615 const_iterator cbegin() const { return m_.cont_.cbegin(); }
616 const_iterator begin() const { return m_.cont_.begin(); }
617 const_iterator cend() const { return m_.cont_.cend(); }
618 const_iterator end() const { return m_.cont_.end(); }
619 reverse_iterator rbegin() { return m_.cont_.rbegin(); }
620 reverse_iterator rend() { return m_.cont_.rend(); }
621 const_reverse_iterator rbegin() const { return m_.cont_.rbegin(); }
622 const_reverse_iterator rend() const { return m_.cont_.rend(); }
624 void clear() { return m_.cont_.clear(); }
625 size_type size() const { return m_.cont_.size(); }
626 size_type max_size() const { return m_.cont_.max_size(); }
627 bool empty() const { return m_.cont_.empty(); }
628 void reserve(size_type s) { return m_.cont_.reserve(s); }
629 void shrink_to_fit() { m_.cont_.shrink_to_fit(); }
630 size_type capacity() const { return m_.cont_.capacity(); }
632 std::pair<iterator,bool> insert(const value_type& value) {
633 return insert(std::move(value_type(value)));
636 std::pair<iterator,bool> insert(value_type&& value) {
637 iterator it = lower_bound(value.first);
638 if (it == end() || value_comp()(value, *it)) {
639 it = get_growth_policy().increase_capacity(m_.cont_, it);
640 return std::make_pair(m_.cont_.insert(it, std::move(value)), true);
642 return std::make_pair(it, false);
645 iterator insert(iterator hint, const value_type& value) {
646 return insert(hint, std::move(value_type(value)));
649 iterator insert(iterator hint, value_type&& value) {
650 return detail::insert_with_hint(*this, m_.cont_, hint, std::move(value),
651 get_growth_policy());
654 template <class InputIterator>
655 void insert(InputIterator first, InputIterator last) {
656 detail::bulk_insert(*this, m_.cont_, first, last);
659 size_type erase(const key_type& key) {
660 iterator it = find(key);
668 void erase(iterator it) {
672 void erase(iterator first, iterator last) {
673 m_.cont_.erase(first, last);
676 iterator find(const key_type& key) {
677 return find(*this, key);
680 const_iterator find(const key_type& key) const {
681 return find(*this, key);
684 template <typename K>
685 if_is_transparent<K, iterator> find(const K& key) {
686 return find(*this, key);
689 template <typename K>
690 if_is_transparent<K, const_iterator> find(const K& key) const {
691 return find(*this, key);
694 mapped_type& at(const key_type& key) {
695 iterator it = find(key);
699 std::__throw_out_of_range("sorted_vector_map::at");
702 const mapped_type& at(const key_type& key) const {
703 const_iterator it = find(key);
707 std::__throw_out_of_range("sorted_vector_map::at");
710 size_type count(const key_type& key) const {
711 return find(key) == end() ? 0 : 1;
714 template <typename K>
715 if_is_transparent<K, size_type> count(const K& key) const {
716 return find(key) == end() ? 0 : 1;
719 iterator lower_bound(const key_type& key) {
720 return lower_bound(*this, key);
723 const_iterator lower_bound(const key_type& key) const {
724 return lower_bound(*this, key);
727 template <typename K>
728 if_is_transparent<K, iterator> lower_bound(const K& key) {
729 return lower_bound(*this, key);
732 template <typename K>
733 if_is_transparent<K, const_iterator> lower_bound(const K& key) const {
734 return lower_bound(*this, key);
737 iterator upper_bound(const key_type& key) {
738 return upper_bound(*this, key);
741 const_iterator upper_bound(const key_type& key) const {
742 return upper_bound(*this, key);
745 template <typename K>
746 if_is_transparent<K, iterator> upper_bound(const K& key) {
747 return upper_bound(*this, key);
750 template <typename K>
751 if_is_transparent<K, const_iterator> upper_bound(const K& key) const {
752 return upper_bound(*this, key);
755 std::pair<iterator, iterator> equal_range(const key_type& key) {
756 return equal_range(*this, key);
759 std::pair<const_iterator, const_iterator> equal_range(
760 const key_type& key) const {
761 return equal_range(*this, key);
764 template <typename K>
765 if_is_transparent<K, std::pair<iterator, iterator>> equal_range(
767 return equal_range(*this, key);
770 template <typename K>
771 if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range(
772 const K& key) const {
773 return equal_range(*this, key);
776 // Nothrow as long as swap() on the Compare type is nothrow.
777 void swap(sorted_vector_map& o) {
778 using std::swap; // Allow ADL for swap(); fall back to std::swap().
782 m_.cont_.swap(o.m_.cont_);
785 mapped_type& operator[](const key_type& key) {
786 iterator it = lower_bound(key);
787 if (it == end() || key_comp()(key, it->first)) {
788 return insert(it, value_type(key, mapped_type()))->second;
793 bool operator==(const sorted_vector_map& other) const {
794 return m_.cont_ == other.m_.cont_;
797 bool operator<(const sorted_vector_map& other) const {
798 return m_.cont_ < other.m_.cont_;
802 // This is to get the empty base optimization; see the comment in
803 // sorted_vector_set.
804 struct EBO : value_compare {
805 explicit EBO(const value_compare& c, const Allocator& alloc)
812 template <typename Self>
813 using self_iterator_t = _t<
814 std::conditional<std::is_const<Self>::value, const_iterator, iterator>>;
816 template <typename Self, typename K>
817 static self_iterator_t<Self> find(Self& self, K const& key) {
818 auto end = self.end();
819 auto it = self.lower_bound(key);
820 if (it == end || !self.key_comp()(key, it->first)) {
826 template <typename Self, typename K>
827 static self_iterator_t<Self> lower_bound(Self& self, K const& key) {
828 auto f = [c = self.key_comp()](value_type const& a, K const& b) {
829 return c(a.first, b);
831 return std::lower_bound(self.begin(), self.end(), key, f);
834 template <typename Self, typename K>
835 static self_iterator_t<Self> upper_bound(Self& self, K const& key) {
836 auto f = [c = self.key_comp()](K const& a, value_type const& b) {
837 return c(a, b.first);
839 return std::upper_bound(self.begin(), self.end(), key, f);
842 template <typename Self, typename K>
843 static std::pair<self_iterator_t<Self>, self_iterator_t<Self>> equal_range(
846 // Note: std::equal_range can't be passed a functor that takes
847 // argument types different from the iterator value_type, so we
849 return {lower_bound(self, key), upper_bound(self, key)};
853 // Swap function that can be found using ADL.
854 template <class K, class V, class C, class A, class G>
855 inline void swap(sorted_vector_map<K,V,C,A,G>& a,
856 sorted_vector_map<K,V,C,A,G>& b) {
860 //////////////////////////////////////////////////////////////////////