# Så här använder du tree containers i C++, del 2

30 juli 2025 | https://www.ribomation.se/blog/2025/sa-har-anvander-du-tree-containers-i-c++-del-2/

Här följer del 2 av artikeln om STL tree containers. I del 1 diskuterade jag gemensamma aspekter hos tree containers, samt användning av `std::set`. I denna del 2, går jag igenom hur du använder `std::map`.

Internt är map tämligen likt set, med en avgörande skillnad: eftersom map parar ihop en nyckel med ett värde, så innehåller varje tree-node ett objekt av typen `std::pair<Key, Val>`. Det här är viktigt att tänka på vid iteration eller när man tankar över element från en map till en annan container.

```cpp
#include <print>
#include <map>
#include <vector>
#include <string>
using namespace std::string_literals;
using std::string;

int main() {
    auto names = std::map<string, int>{
        {"Anna"s, 10}, {"Berit"s, 20}, {"Carin"s, 30}
    };

    std::print("explicit  : [");
    for (std::pair<string const, int>& p : names) std::print("({}: {}) ", p.first, p.second);
    std::println("]");

    std::print("implicit  : [");
    for (auto const& p : names) std::print("({}: {}) ", p.first, p.second);
    std::println("]");

    std::print("simple    : [");
    for (auto const& [name, count] : names) std::print("({}: {}) ", name, count);
    std::println("]");

    std::println("convenient: {}", names);

    auto v = std::vector< std::pair<string const, int> >{names.begin(), names.end()};
    std::println("vector    : {}", v);
}
```

```shell
explicit  : [(Anna: 10) (Berit: 20) (Carin: 30) ]
implicit  : [(Anna: 10) (Berit: 20) (Carin: 30) ]
simple    : [(Anna: 10) (Berit: 20) (Carin: 30) ]
convenient: {"Anna": 10, "Berit": 20, "Carin": 30}
vector    : [("Anna", 10), ("Berit", 20), ("Carin", 30)]
```

### Åtkomst av element

Precis som för std::set, så har std::map metoderna `contains()`, `count()` och `find()`, med likartad funktion.

```cpp
auto names = std::map<string, int>{
    {"Anna"s, 10}, {"Berit"s, 20}, {"Carin"s, 30}
};

std::println("-- using contains() --");
if (names.contains("Carin")) std::println("snames contains 'Carin'");
if (not names.contains("Nisse")) std::println("names contains not 'Nisse'");

std::println("-- using count() (pre c++20) --");
if (names.count("Anna") > 0) std::println("snames contains 'Anna'");
if (names.count("Frida") == 0) std::println("names contains not 'Frida'");

std::println("-- using find() --");
if (auto it = names.find("Berit"s); it != names.end())
    std::println("found: {}", *it);
if (auto it = names.find("Kurt"s); it == names.end())
    std::println("not found: {}", "Kurt"s);
```

```shell
-- using contains() --
snames contains 'Carin'
names contains not 'Nisse'
-- using count() (pre c++20) --
snames contains 'Anna'
names contains not 'Frida'
-- using find() --
found: ("Berit", 20)
not found: Kurt
```

Emellertid, är det index operatorn som är mest användbar. Den returnerar referensen till värdet för en viss nyckel, eller om nyckel saknas så skapas ett nytt par där värdet initieras med typens default-constructor.

Alternativt, så finns metoden `at()` som också returner en referens till värdet, men till skillnad mot index operatorn, så kastas en exception om nyckeln inte finns i containern. Här kommer ett belysande kodexempel.

```cpp
auto names = std::map<string, int>{
    {"Anna"s, 10}, {"Berit"s, 20}, {"Carin"s, 30}
};

std::println("-- using operator[] --");
names["Berit"s] = 50;
names["Carin"s] *= 5;
names["Anna"s]++;
auto _ = names["Doris"s];
std::println("names: {}", names);

std::println("-- using method at() --");
names.at("Anna"s) += 10;
try {
    auto _ = names.at("Eva"s);
} catch (std::exception const& x) {
    std::println("ERR: {}", x.what());
}
std::println("names: {}", names);
```

```shell
-- using operator[] --
names: {"Anna": 11, "Berit": 50, "Carin": 150, "Doris": 0}
-- using method at() --
ERR: map::at
names: {"Anna": 21, "Berit": 50, "Carin": 150, "Doris": 0}
```

### Insättning av element

Precis som för set, så finns metoderna `insert()` och `emplace()`, om det inte det räcker med index operatorn. Först visar jag ett exempel med insert(). Notera att det du sätter in är ett object av typen `std::pair`.

```cpp
auto names = std::map<string, int>{};

names.insert( {"Berit"s, 20} );
names.insert( std::pair{"Carin"s, 30} );
names.insert( std::make_pair("Anna"s, 10) );

auto v = std::vector< std::pair<string const, int> >{
    {"Frida"s, 60}, {"Doris"s, 40}, {"Eva"s, 50}
};
names.insert(v.begin(), v.end());

std::println("names: {}", names);
// names: {"Anna": 10, "Berit": 20, "Carin": 30, "Doris": 40, "Eva": 50, "Frida": 60}
```

Användning av metoden emplace() är emellertid av mer begränsat värde, såvida inte den behövs av strikt prestandamässiga skäl. Här visar jag två anrop. Det första tar nyckel plus ett färdigt objekt (Person), medan det andra tar konstruktor-argumenten via ett tämligen krångligt och pratsamt sätt. Första argumentet till emplace är en så kallad _class tag_, och signalerar till emplace att packa upp de följande två tuple argumenten och skapa objekten på plats inuti containern. Personligen tycker jag man klarar sig alldeles utmärkt med att bara använda index operator för både insättning och åtkomst.

```cpp
auto persons = std::map<string, Person>{};

persons.emplace("anna"s, Person{"Anna Conda"s, 42, 165.F});

persons.emplace(
    std::piecewise_construct,
    std::forward_as_tuple("per"s),
    std::forward_as_tuple("Per Silja"s, 52U, 175.F)
);

for (auto&& [key, p] : persons) std::println("{:4}: {}, {}, {}", 
    key, p.name, p.age, p.height);
// anna: Anna Conda, 42, 165
// per : Per Silja, 52, 175
```
