Ch10.2: Creating a Container and Inserting Keys
1. Creating One
A default-constructed container is empty and costs nothing beyond a few null pointers — no allocation happens until you insert something:
#include <fast_io_dsal/str_btree_set.h>
#include <fast_io.h>
using namespace ::fast_io::io;
int main()
{
::fast_io::str_btree_set s;
println("empty: ", s.is_empty());
}
empty: 1
is_empty() exists on all six containers. Note that it is
is_empty(), not empty().
From a list of keys
A set accepts an initializer list of views. Because the element type is
::fast_io::string_view, you write each key as its own braced
element:
::fast_io::str_btree_set s{{::fast_io::string_view("gamma"),
::fast_io::string_view("alpha")}};
for (auto e : s) { println(e); }
alpha
gamma
A map takes key/value pairs, and here string literals work directly because the pair type is constructed for you:
::fast_io::str_btree_map<::std::size_t> m{{"b", 2}, {"a", 1}};
From any range
from_range takes anything with begin()/end():
::fast_io::vector<::fast_io::cstring_view> words{
::fast_io::cstring_view("delta"), ::fast_io::cstring_view("alpha")};
::fast_io::str_btree_set s(::std::from_range, words);
On an existing container, insert_range does the same job:
::fast_io::str_btree_set s;
s.insert_range(words);
2. insert_key and What It Returns
insert_key adds one key. A key that is already present is
not an error and does not produce a duplicate element.
The return value tells you which of the two happened, which is why it is
not void:
::fast_io::str_btree_set s;
println(s.insert_key("alpha")); // 1: key was new, container grew
println(s.insert_key("beta")); // 1
println(s.insert_key("alpha")); // 0: already there, nothing changed
println(s.contains("beta")); // 1
1
1
0
1
true means a new element appeared, false means the
key was already present.
The Swiss-table containers return a small struct instead, because they can also give you the position of the element:
::fast_io::str_swiss_set ss;
auto r{ss.insert_key("alpha")};
println(r.inserted); // 1
println(*r.position); // alpha — r.position is an iterator
Ignoring the return value is fine and common:
s.insert_key(k);. It is [[nodiscard]]-free on
purpose — membership is usually not interesting at the call site.
3. Inserting Into a Map
A map needs a value along with the key. Three functions, three meanings:
| Call | Key exists | Key is new | Returns |
|---|---|---|---|
insert_key(k, v) |
Does nothing, keeps old value. | Inserts v. |
bool: did it insert? |
insert_key_or_assign(k, v) |
Overwrites the old value. | Inserts v. |
bool: did it insert (rather than assign)? |
emplace_key(k, args...) |
Does nothing. | Constructs the value in place from args. |
bool: did it insert? |
::fast_io::str_btree_map<::std::size_t> m{{"ant", 1zu}};
m.insert_key("ant", 100zu); // ignored: "ant" is already there
println(m.find("ant")->mapped()); // 1
m.insert_key_or_assign("ant", 7zu); // overwrites
println(m.find("ant")->mapped()); // 7
m.emplace_key("bee", 42zu); // constructs the value in place
println(m.find("bee")->mapped()); // 42
4. Bulk Insertion and Hints
insert_range inserts a whole range. For the B-tree family there
is also insert_key_hint(hint, key): if you already know roughly
where the key belongs, the hint lets the container start its search there
instead of at the root.
The common special case is appending keys that arrive in sorted
order — reading a sorted file, for instance. Passing
cend() as the hint is the fast path for that:
::fast_io::str_btree_set s;
s.insert_key_hint(s.cend(), "b");
s.insert_key_hint(s.cend(), "c");
s.insert_key("a"); // out of order: normal insertion
for (auto e : s) { println(e); }
a
b
c
insert_range already uses this hint internally, so filling a
B-tree from a sorted source through insert_range gets the same
benefit without you writing anything special. A hint that turns out to be
wrong is never an error — the container simply falls back to a normal
insertion.
5. Copying, Moving, Clearing
All six containers are copyable and movable. Copying deep-copies every key (each key gets its own allocation); moving transfers the internal pointers and leaves the source empty:
::fast_io::str_btree_set a{{::fast_io::string_view("x"), ::fast_io::string_view("y")}};
::fast_io::str_btree_set b{a}; // deep copy
::fast_io::str_btree_set c{::std::move(b)}; // b is now empty
println(a.contains("x"), b.contains("x"), c.contains("x")); // 1 0 1
clear() removes every element and keeps the object usable;
clear_destroy() does the same but additionally releases the
container’s own storage so the next insert starts from scratch. Both
are noexcept. For a container you are about to refill, prefer
clear().
s.clear();
println(s.is_empty()); // 1
s.insert_key("again"); // works as before
Key takeaways
- Default construction is free: no allocation until the first insert.
- Use
is_empty()to test for emptiness. - Sets can be built from an initializer list of
string_views, maps from{key, value}pairs. from_range/insert_rangeaccept any range of views.insert_keynever creates duplicates; itsboolsays whether the container grew.- Maps:
insert_keykeeps the old value,insert_key_or_assignoverwrites,emplace_keyconstructs in place. - B-tree containers accept
insert_key_hint(cend(), key)for in-order bulk filling. - Copying deep-copies keys; moving transfers ownership;
clear()empties but keeps the object usable.