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
Read the boolean as “did the container grow?” That single rule covers every insert function in this chapter: 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
“Key exists” compares the bytes of the key, not a pointer and not a hash of the object identity. Keys are compared exactly: case-sensitive, no Unicode normalisation, no trimming. If you want case-insensitive keys, normalise the string before inserting and before every lookup.

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