Expanded flat_set/map section

This commit is contained in:
Ion Gaztañaga
2026-06-07 00:42:36 +02:00
parent b9f10e7c83
commit 22337cbc2c
2 changed files with 109 additions and 2 deletions
+40 -2
View File
@@ -413,8 +413,8 @@ its erase functions (AssocVector::erase invalidates all iterators into the objec
complexity guarantees of insert and erase (linear as opposed to constant). ]]
[*Boost.Container] [classref boost::container::flat_map flat_map], [classref boost::container::flat_set flat_set], [classref boost::container::flat_multimap flat_multimap] and [classref boost::container::flat_multiset flat_multiset] containers are ordered, vector-like container based, associative
containers following Austern's and Alexandrescu's guidelines. These ordered vector containers have also
benefited with the addition of `move semantics` to C++11, speeding up insertion and
containers following Austern's and Alexandrescu's guidelines. These ordered vector-like containers have also
benefited with the addition of `move semantics`, speeding up insertion and
erasure times considerably. Flat associative containers have the following attributes:
* Faster lookup than standard associative containers
@@ -428,6 +428,44 @@ erasure times considerably. Flat associative containers have the following attri
(copy/move constructors can throw when shifting values in erasures and insertions)
* Slower insertion and erasure than standard associative containers (specially for non-movable types)
[*Differences with the standard `std::flat_map`/`std::flat_set`]. C++23 added `std::flat_map`, `std::flat_set`,
`std::flat_multimap` and `std::flat_multiset`, based on the same sorted-vector idea. There are, however, several
notable differences with [*Boost.Container]'s long-standing implementation (available since 2004 and usable from
C++03 onwards):
* [*Container vs. container adaptor]: [*Boost.Container]'s flat containers are full-fledged containers that own a
single underlying sorted sequence. The standard ones are container [*adaptors] layered on top of user-provided
sequence containers, exposed through `keys()`/`values()` accessors and `extract()`/`replace()` operations.
* [*Storage layout for maps] (array of structs vs. structure of arrays):
[classref boost::container::flat_map flat_map] stores its elements as a single sequence of `value_type` =
`std::pair<Key, T>`, so each key is interleaved with its mapped value in one contiguous buffer. `std::flat_map`
instead keeps keys and mapped values in two [*separate] parallel containers (`key_container_type` and
`mapped_container_type`). The standard layout can speed up key-only scans (lookups touch only the keys array) at
the cost of an extra indirection when both key and value are needed, while Boost's layout keeps each key next to
its value.
* [*Iterators and `value_type`]: because [classref boost::container::flat_map flat_map] holds real
`std::pair<Key, T>` objects, its iterators dereference to actual pair lvalues and `&*it` yields a pointer to a
stored pair. `std::flat_map`, having two arrays, exposes proxy references of type
`pair<const key_type&, mapped_type&>` and does not provide pointers into a single pair array.
* [*Underlying sequence access]: [*Boost.Container] exposes the whole sorted vector through `sequence_type`,
`extract_sequence()` and `adopt_sequence()` (optionally with the `ordered_unique_range_t` tag for an O(1)
adoption), which is handy to build the container cheaply and then re-adopt it. The standard counterpart uses
`extract()`/`replace()` returning the key and mapped containers separately.
* [*Availability and configurability]: [*Boost.Container]'s flat containers work from C++03, let you choose the
underlying vector-like sequence (e.g. [classref boost::container::small_vector small_vector] or
[classref boost::container::static_vector static_vector]) through a template parameter and offer additional
ordered-range insertion overloads.
The following example shows the most common operations, the single-sequence storage and the
`extract_sequence`/`adopt_sequence` idiom:
[import ../example/doc_flat_map.cpp]
[doc_flat_map]
[endsect]
[section:devector ['devector]]
+69
View File
@@ -0,0 +1,69 @@
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2024-2024. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/container for documentation.
//
//////////////////////////////////////////////////////////////////////////////
//[doc_flat_map
#include <boost/container/flat_map.hpp>
#include <boost/move/utility_core.hpp> //boost::move
//Make sure assertions are active
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <cassert>
int main ()
{
using namespace boost::container;
typedef flat_map<int, int> map_t;
map_t m;
//Like vector, we can reserve storage to avoid reallocations while filling.
m.reserve(8);
//Insertions keep the underlying vector sorted by key. They are O(N)
//because the elements after the insertion point must be shifted.
m[30] = 3;
m[10] = 1;
m[20] = 2;
assert(m.size() == 3);
//Iteration is in key order and over contiguous memory, using random-access
//iterators, so it is much faster than a node-based std::map.
{
map_t::const_iterator it = m.begin();
assert(it->first == 10 && it->second == 1); ++it;
assert(it->first == 20 && it->second == 2); ++it;
assert(it->first == 30 && it->second == 3);
}
//Lookup uses binary search: O(log N).
map_t::iterator f = m.find(20);
assert(f != m.end() && f->second == 2);
//All values live in a single underlying sequence of value_type, which is
//std::pair<Key, T> (an array of structs). This is a key difference with the
//C++23 std::flat_map, a container *adaptor* that keeps keys and mapped
//values in two separate, parallel containers (a structure of arrays).
const map_t::value_type *raw = &*m.begin();
assert(raw[0].first == 10 && raw[2].first == 30);
//The underlying sorted vector can be moved out with extract_sequence() and
//moved back in with adopt_sequence(). Because the extracted sequence is
//already ordered and free of duplicates, we can re-adopt it in O(1) using
//the ordered_unique_range_t overload.
map_t::sequence_type seq = m.extract_sequence();
assert(m.empty());
m.adopt_sequence(ordered_unique_range_t(), boost::move(seq));
assert(m.size() == 3 && m.find(30) != m.end());
return 0;
}
//]