Skip to content

Commit ca4daaa

Browse files
committed
book: Chapter 4 since markers (containers)
Add 'since C++NN' markers to the container features. The existing motivation (std::array vs vector, the tuple intro, string_view/byte/pmr sections) already reads well, so no prose rewrite was needed here.
1 parent 47b41f3 commit ca4daaa

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

book/en-us/04-containers.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ order: 4
1212

1313
### `std::array`
1414

15+
*(since C++11)*
16+
1517
When you see this container, you will have this problem:
1618

1719
1. Why introduce `std::array` instead of `std::vector` directly?
@@ -103,6 +105,8 @@ std::sort(arr.begin(), arr.end());
103105

104106
### `std::forward_list`
105107

108+
*(since C++11)*
109+
106110
`std::forward_list` is a list container, and the usage is similar to `std::list`, so we don't spend a lot of time introducing it.
107111

108112
Need to know is that, unlike the implementation of the doubly linked list of `std::list`, `std::forward_list` is implemented using a singly linked list.
@@ -111,6 +115,8 @@ It is also the only container in the standard library container that does not pr
111115

112116
## 4.2 Unordered Container
113117

118+
*(since C++11)*
119+
114120
We are already familiar with the ordered container `std::map`/`std::set` in traditional C++. These elements are internally implemented by red-black trees.
115121
The average complexity of inserts and searches is `O(log(size))`. When inserting an element, the element size is compared according to the `<` operator and the element is determined to be the same.
116122
And select the appropriate location to insert into the container. When traversing the elements in this container, the output will be traversed one by one in the order of the `<` operator.
@@ -177,6 +183,8 @@ But the flaw of `std::pair` is obvious, only two elements can be saved.
177183

178184
### Basic Operations
179185

186+
*(since C++11)*
187+
180188
There are three core functions for the use of tuples:
181189

182190
1. `std::make_tuple`: construct tuple
@@ -330,6 +338,8 @@ iterate_tuple([](const auto& v) { std::cout << v << ' '; }, new_tuple);
330338
331339
### `std::string_view`
332340
341+
*(since C++17)*
342+
333343
`std::string_view`, introduced in C++17, is a **non-owning, read-only** view over a sequence of characters; it holds just a pointer and a length. Declaring a parameter as `std::string_view` accepts both a `std::string` and a string literal, **without any copy or allocation**:
334344
335345
```cpp
@@ -350,6 +360,8 @@ Mind its **lifetime**: a `string_view` does not own the underlying data, so the
350360

351361
### `std::byte`
352362

363+
*(since C++17)*
364+
353365
`std::byte` represents a single byte of **raw memory**. Unlike `char` or `unsigned char`, it is not an arithmetic type — the standard defines only bitwise operators for it, which prevents accidental arithmetic on raw bytes at the type level:
354366

355367
```cpp
@@ -363,6 +375,8 @@ int v = std::to_integer<int>(b); // explicit conversion to an integer: 49
363375
364376
## 4.5 Associative container improvements
365377
378+
*(since C++17)*
379+
366380
C++17 added several more precise and more efficient operations to associative containers such as `std::map` / `std::unordered_map`:
367381
368382
- `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".
@@ -387,6 +401,8 @@ m.merge(more); // splice more's nodes into m
387401

388402
## 4.6 Polymorphic allocators `std::pmr`
389403

404+
*(since C++17)*
405+
390406
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.
391407

392408
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:

book/zh-cn/04-containers.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ order: 4
1212

1313
### `std::array`
1414

15+
*(C++11)*
16+
1517
看到这个容器的时候肯定会出现这样的问题:
1618

1719
1. 为什么要引入 `std::array` 而不是直接使用 `std::vector`
@@ -100,6 +102,8 @@ std::sort(arr.begin(), arr.end());
100102

101103
### `std::forward_list`
102104

105+
*(C++11)*
106+
103107
`std::forward_list` 是一个列表容器,使用方法和 `std::list` 基本类似,因此我们就不花费篇幅进行介绍了。
104108

105109
需要知道的是,和 `std::list` 的双向链表的实现不同,`std::forward_list` 使用单向链表进行实现,
@@ -108,6 +112,8 @@ std::sort(arr.begin(), arr.end());
108112

109113
## 4.2 无序容器
110114

115+
*(C++11)*
116+
111117
我们已经熟知了传统 C++ 中的有序容器 `std::map`/`std::set`,这些元素内部通过红黑树进行实现,
112118
插入和搜索的平均复杂度均为 `O(log(size))`。在插入元素时候,会根据 `<` 操作符比较元素大小并判断元素是否相同,
113119
并选择合适的位置插入到容器中。当对这个容器中的元素进行遍历时,输出结果会按照 `<` 操作符的顺序来逐个遍历。
@@ -174,6 +180,8 @@ Key:[3] Value:[3]
174180

175181
### 元组基本操作
176182

183+
*(C++11)*
184+
177185
关于元组的使用有三个核心的函数:
178186

179187
1. `std::make_tuple`: 构造元组
@@ -330,6 +338,8 @@ iterate_tuple([](const auto& v) { std::cout << v << ' '; }, new_tuple);
330338
331339
### `std::string_view`
332340
341+
*(C++17)*
342+
333343
C++17 引入的 `std::string_view` 是对一段字符序列的**非拥有 (non-owning)、只读**视图,它仅保存一个指针和一个长度。把函数形参写成 `std::string_view` 既可以接受 `std::string`,也可以接受字符串字面量,而且**不会发生任何拷贝或内存分配**:
334344
335345
```cpp
@@ -350,6 +360,8 @@ print(s); // 隐式转换,无拷贝
350360

351361
### `std::byte`
352362

363+
*(C++17)*
364+
353365
`std::byte` 用于表示一段**原始内存**中的一个字节。与 `char``unsigned char` 不同,它不是算术类型——标准只为其定义了位运算符,从而在类型层面避免了对原始字节进行意外的算术运算:
354366

355367
```cpp
@@ -363,6 +375,8 @@ int v = std::to_integer<int>(b); // 需要显式转换为整数:49
363375
364376
## 4.5 关联容器的改进
365377
378+
*(C++17)*
379+
366380
C++17 为 `std::map` / `std::unordered_map` 等关联容器增加了若干更精确、也更高效的操作:
367381
368382
- `try_emplace`:仅当键不存在时才插入;当键已存在时,它**不会**修改已有的值,也不会从实参中移动,因此比 `emplace` 更适合「不存在则插入」的场景。
@@ -387,6 +401,8 @@ m.merge(more); // 将 more 的节点拼接进 m
387401

388402
## 4.6 多态分配器 `std::pmr`
389403

404+
*(C++17)*
405+
390406
C++17 在 `<memory_resource>` 中引入了 `std::pmr` 命名空间,提供了基于**内存资源 (memory resource)** 的多态分配器。它把「从哪里分配内存」这一策略与容器类型解耦:不同内存资源支撑的同一种 `pmr` 容器仍然是同一个类型,从而避免了模板分配器带来的类型膨胀。
391407

392408
例如,`std::pmr::monotonic_buffer_resource` 可以从一块预先准备好的缓冲区(甚至是栈上的缓冲区)中分配内存,直到资源析构时才统一释放,非常适合分配密集且生命周期一致的场景:

0 commit comments

Comments
 (0)