Skip to content

Commit 3ab6142

Browse files
committed
book: add C++17 associative container improvements and std::pmr
Section 4.5 covers try_emplace / insert_or_assign and node-based extract / merge; section 4.6 introduces polymorphic allocators (std::pmr) with a monotonic_buffer_resource example. Compiled examples code/4/4.6-4.7. Refs #1
1 parent 6690875 commit 3ab6142

4 files changed

Lines changed: 151 additions & 0 deletions

File tree

‎book/en-us/04-containers.md‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,49 @@ b |= std::byte{0b0000'0001}; // 49
361361
int v = std::to_integer<int>(b); // explicit conversion to an integer: 49
362362
```
363363
364+
## 4.5 Associative container improvements
365+
366+
C++17 added several more precise and more efficient operations to associative containers such as `std::map` / `std::unordered_map`:
367+
368+
- `try_emplace`: inserts only when the key is absent; when the key already exists it does **not** modify the existing value nor move from its arguments, which makes it better than `emplace` for "insert if not present".
369+
- `insert_or_assign`: inserts a new element, or **overwrites** the value when the key already exists, returning whether an insertion happened.
370+
- Node-based operations `extract` / `merge`: `extract` detaches a node from the container without copying or moving the element, and `merge` splices nodes from another container directly in.
371+
372+
```cpp
373+
#include <map>
374+
#include <string>
375+
376+
std::map<int, std::string> m;
377+
m.try_emplace(1, "one");
378+
m.try_emplace(1, "uno"); // no effect, key 1 already present
379+
m.insert_or_assign(1, "ONE"); // overwrites with "ONE"
380+
381+
std::map<int, std::string> other;
382+
other.insert(m.extract(1)); // move node 1 into other, no element copy
383+
384+
std::map<int, std::string> more{{3, "three"}};
385+
m.merge(more); // splice more's nodes into m
386+
```
387+
388+
## 4.6 Polymorphic allocators `std::pmr`
389+
390+
C++17 introduced the `std::pmr` namespace in `<memory_resource>`, providing polymorphic allocators based on a **memory resource**. It decouples the *where to allocate* policy from the container type: the same `pmr` container backed by different memory resources is still a single type, avoiding the type bloat caused by template allocators.
391+
392+
For instance, `std::pmr::monotonic_buffer_resource` can allocate from a pre-prepared buffer (even one on the stack) and frees everything only when the resource is destroyed — ideal for allocation-heavy workloads with a shared lifetime:
393+
394+
```cpp
395+
#include <array>
396+
#include <cstddef>
397+
#include <memory_resource>
398+
#include <vector>
399+
400+
std::array<std::byte, 1024> buffer;
401+
std::pmr::monotonic_buffer_resource pool{buffer.data(), buffer.size()};
402+
403+
std::pmr::vector<int> v{&pool}; // allocates from the stack buffer, not the heap
404+
for (int i = 0; i < 5; ++i) v.push_back(i);
405+
```
406+
364407
## Conclusion
365408
366409
This chapter briefly introduces the new containers in modern C++. Their usage is similar to that of the existing containers in C++. It is relatively simple, and you can choose the containers you need to use according to the actual scene, to get better performance.

‎book/zh-cn/04-containers.md‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,49 @@ b |= std::byte{0b0000'0001}; // 49
361361
int v = std::to_integer<int>(b); // 需要显式转换为整数:49
362362
```
363363
364+
## 4.5 关联容器的改进
365+
366+
C++17 为 `std::map` / `std::unordered_map` 等关联容器增加了若干更精确、也更高效的操作:
367+
368+
- `try_emplace`:仅当键不存在时才插入;当键已存在时,它**不会**修改已有的值,也不会从实参中移动,因此比 `emplace` 更适合「不存在则插入」的场景。
369+
- `insert_or_assign`:插入新元素,或在键已存在时**覆盖**其值,并返回是否发生了插入。
370+
- 基于节点的操作 `extract` / `merge`:`extract` 可以把一个节点从容器中「摘下」而不发生元素的拷贝或移动,`merge` 则能把另一个容器的节点直接「拼接」过来。
371+
372+
```cpp
373+
#include <map>
374+
#include <string>
375+
376+
std::map<int, std::string> m;
377+
m.try_emplace(1, "one");
378+
m.try_emplace(1, "uno"); // 无效果,键 1 已存在
379+
m.insert_or_assign(1, "ONE"); // 覆盖为 "ONE"
380+
381+
std::map<int, std::string> other;
382+
other.insert(m.extract(1)); // 将节点 1 移动到 other,不拷贝元素
383+
384+
std::map<int, std::string> more{{3, "three"}};
385+
m.merge(more); // 将 more 的节点拼接进 m
386+
```
387+
388+
## 4.6 多态分配器 `std::pmr`
389+
390+
C++17 在 `<memory_resource>` 中引入了 `std::pmr` 命名空间,提供了基于**内存资源 (memory resource)** 的多态分配器。它把「从哪里分配内存」这一策略与容器类型解耦:不同内存资源支撑的同一种 `pmr` 容器仍然是同一个类型,从而避免了模板分配器带来的类型膨胀。
391+
392+
例如,`std::pmr::monotonic_buffer_resource` 可以从一块预先准备好的缓冲区(甚至是栈上的缓冲区)中分配内存,直到资源析构时才统一释放,非常适合分配密集且生命周期一致的场景:
393+
394+
```cpp
395+
#include <array>
396+
#include <cstddef>
397+
#include <memory_resource>
398+
#include <vector>
399+
400+
std::array<std::byte, 1024> buffer;
401+
std::pmr::monotonic_buffer_resource pool{buffer.data(), buffer.size()};
402+
403+
std::pmr::vector<int> v{&pool}; // 从栈上的 buffer 分配,而非堆
404+
for (int i = 0; i < 5; ++i) v.push_back(i);
405+
```
406+
364407
## 总结
365408
366409
本章简单介绍了现代 C++ 中新增的容器,它们的用法和传统 C++ 中已有的容器类似,相对简单,可以根据实际场景丰富的选择需要使用的容器,从而获得更好的性能。
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// 4.6.associative.improvements.cpp
3+
// chapter 04 containers
4+
// modern c++ tutorial
5+
//
6+
// created by changkun at changkun.de
7+
// https://github.com/changkun/modern-cpp-tutorial
8+
//
9+
10+
#include <iostream>
11+
#include <map>
12+
#include <string>
13+
14+
int main() {
15+
std::map<int, std::string> m;
16+
17+
// try_emplace: only inserts if the key is absent; does not touch
18+
// the mapped value (or move from arguments) when the key exists.
19+
m.try_emplace(1, "one");
20+
m.try_emplace(1, "uno"); // no effect, key 1 already present
21+
22+
// insert_or_assign: inserts, or overwrites the existing value.
23+
m.insert_or_assign(1, "ONE");
24+
m.insert_or_assign(2, "TWO");
25+
26+
// extract: detach a node without copying/moving the element, then
27+
// merge nodes from another map.
28+
std::map<int, std::string> other;
29+
other.insert(m.extract(2)); // move node 2 into `other`
30+
31+
std::map<int, std::string> more{{3, "three"}};
32+
m.merge(more); // splice nodes from `more` into `m`
33+
34+
for (const auto& [k, v] : m)
35+
std::cout << k << " => " << v << "\n";
36+
std::cout << "other has key 2: " << other.count(2) << "\n";
37+
}

‎code/4/4.7.pmr.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// 4.7.pmr.cpp
3+
// chapter 04 containers
4+
// modern c++ tutorial
5+
//
6+
// created by changkun at changkun.de
7+
// https://github.com/changkun/modern-cpp-tutorial
8+
//
9+
10+
#include <array>
11+
#include <cstddef>
12+
#include <iostream>
13+
#include <memory_resource>
14+
#include <vector>
15+
16+
int main() {
17+
// A monotonic_buffer_resource hands out memory from a fixed buffer
18+
// (here on the stack) and frees nothing until destroyed. The pmr
19+
// container allocates through the resource instead of the heap.
20+
std::array<std::byte, 1024> buffer;
21+
std::pmr::monotonic_buffer_resource pool{buffer.data(), buffer.size()};
22+
23+
std::pmr::vector<int> v{&pool};
24+
for (int i = 0; i < 5; ++i) v.push_back(i);
25+
26+
for (int x : v) std::cout << x << ' ';
27+
std::cout << std::endl;
28+
}

0 commit comments

Comments
 (0)