{"id":19371283,"url":"https://github.com/mq-b/cpp20-stl-cookbook-src","last_synced_at":"2025-04-07T08:14:48.270Z","repository":{"id":65275497,"uuid":"588541449","full_name":"Mq-b/Cpp20-STL-Cookbook-src","owner":"Mq-b","description":"C++20 STL Cookbook","archived":false,"fork":false,"pushed_at":"2024-09-13T12:04:49.000Z","size":21068,"stargazers_count":592,"open_issues_count":0,"forks_count":81,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-31T07:04:23.783Z","etag":null,"topics":["cpp20-library"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Mq-b.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-13T11:23:50.000Z","updated_at":"2025-03-30T13:43:02.000Z","dependencies_parsed_at":"2024-09-13T23:47:59.362Z","dependency_job_id":"5c8d3619-5f1b-4a2d-a111-a1334a5c28b2","html_url":"https://github.com/Mq-b/Cpp20-STL-Cookbook-src","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mq-b%2FCpp20-STL-Cookbook-src","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mq-b%2FCpp20-STL-Cookbook-src/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mq-b%2FCpp20-STL-Cookbook-src/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mq-b%2FCpp20-STL-Cookbook-src/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Mq-b","download_url":"https://codeload.github.com/Mq-b/Cpp20-STL-Cookbook-src/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247615377,"owners_count":20967184,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp20-library"],"created_at":"2024-11-10T08:17:51.974Z","updated_at":"2025-04-07T08:14:48.248Z","avatar_url":"https://github.com/Mq-b.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 《C++20 STL Cookbook》2023\n\n###  环配置\n**Visual Studio Enterprise 2022(64位) 版本 17.4.3**\n\n**CMake 3.8**\n\n**R7 5700U** 八核心十六线程\n\n###  [B站视频讲解](https://www.bilibili.com/video/BV1r8411N75b/?spm_id_from=333.999.0.0\u0026vd_source=1992ca910d6cd0582931f6f985dc7fa0)\n\n早期制作，内容些许过时，视频全损音质，画质xx，需注意。\n\n\u003cbr\u003e\n\n- [《C++20 STL Cookbook》2023](#c20-stl-cookbook2023)\n\t\t- [环境](#环境)\n\t\t- [B站视频讲解](#b站视频讲解)\n\t- [第一章 C++20的新特性](#第一章-c20的新特性)\n\t\t- [1.2格式化文本](#12格式化文本)\n\t\t- [1.3使用编译时constexpr `std::vector`和`std::string`](#13使用编译时constexpr-stdvector和stdstring)\n\t\t- [1.4安全比较不同类型的整数`cmp_less`](#14安全比较不同类型的整数cmp_less)\n\t\t- [1.5三路比较运算符](#15三路比较运算符)\n\t\t- [1.6查找特性测试宏](#16查找特性测试宏)\n\t\t- [1.7概念(`concept`)和约束(`constraint`)-创建更安全的模板](#17概念concept和约束constraint-创建更安全的模板)\n\t\t- [1.8模块](#18模块)\n\t\t- [1.9视图](#19视图)\n\t\t- [第一章总结](#第一章总结)\n\t- [第二章 STL的泛型特性](#第二章-stl的泛型特性)\n\t\t- [2.2`std::span`类](#22span类)\n\t\t- [2.3结构化绑定](#23结构化绑定)\n\t\t- [2.4`if`\\\u0026`switch`中的初始化](#24ifswitch中的初始化)\n\t\t- [2.5模板参数推导（CTAD）](#25模板参数推导ctad)\n\t\t- [2.6编译期`if`](#26编译期if)\n\t\t- [第二章总结](#第二章总结)\n\t- [第三章 STL容器](#第三章-stl容器)\n\t\t- [3.3使用擦除函数从容器中擦除项](#33使用擦除函数从容器中擦除项)\n\t\t- [3.4常数时间内从未排序的向量中删除项](#34常数时间内从未排序的向量中删除项)\n\t\t- [3.5安全的访问`std::vector`元素](#35安全的访问stdvector元素)\n\t\t- [3.6保持`std::vector`元素的顺序](#36保持stdvector元素的顺序)\n\t\t- [3.7高效的将元素插入到`std::map`中](#37高效的将元素插入到stdmap中)\n\t\t- [3.8高效的修改`std::map`项的键值](#38高效的修改stdmap项的键值)\n\t\t- [3.9自定义键值的`std::unordered_map`](#39自定义键值的stdunordered_map)\n\t\t- [3.10使用`std::set`进行输入和筛选](#310使用set进行输入和筛选)\n\t\t- [3.11实现简单的RPN计算器与`std::deque`](#311实现简单的rpn计算器与deque)\n\t\t- [3.12使用`std::map`的词频计数器](#312使用stdmap的词频计数器)\n\t\t- [3.13找出含有相应长句的`std::vector`](#313找出含有相应长句的stdvector)\n\t\t- [3.14使用`std::multimap`制作待办事项](#314使用stdmultimap制作待办事项)\n\t\t- [第三章总结](#第三章总结)\n\t- [第四章 兼容迭代器](#第四章-兼容迭代器)\n\t\t- [4.3创建可迭代范围](#43创建可迭代范围)\n\t\t- [4.4使迭代器与STL迭代器特性兼容](#44使迭代器与stl迭代器特性兼容)\n\t\t- [4.5使用迭代器适配器填充STL容器](#45使用迭代器适配器填充stl容器)\n\t\t- [4.6创建一个迭代器生成器](#46创建一个迭代器生成器)\n\t\t- [4.7反向迭代器的反向适配器](#47反向迭代器的反向适配器)\n\t\t- [4.8用哨兵迭代未知长度的对象](#48用哨兵迭代未知长度的对象)\n\t\t- [4.9构建zip迭代器适配器](#49构建zip迭代器适配器)\n\t\t- [4.10创建随机访问迭代器](#410创建随机访问迭代器)\n\t\t- [第四章总结](#第四章总结)\n\t- [第五章 lambda表达式](#第五章-lambda表达式)\n\t\t- [5.3用于作用域可重用代码](#53用于作用域可重用代码)\n\t\t- [5.4算法库中作为谓词](#54算法库中作为谓词)\n\t\t- [5.5与`std::function`一起作为多态包装器](#55与stdfunction一起作为多态包装器)\n\t\t- [5.6用递归连接lambda](#56用递归连接lambda)\n\t\t- [5.7将谓词与逻辑连接词连接起来](#57将谓词与逻辑连接词连接起来)\n\t\t- [5.8用相同的输入调用多个lambda](#58用相同的输入调用多个lambda)\n\t\t- [5.9对跳转表使用映射lambda](#59对跳转表使用映射lambda)\n\t\t- [第五章总结](#第五章总结)\n\t- [第六章 STL算法](#第六章-stl算法)\n\t\t- [6.2基于迭代器的复制](#62基于迭代器的复制)\n\t\t- [6.3将容器元素连接到以供字符串当中](#63将容器元素连接到以供字符串当中)\n\t\t- [6.4`std::sort`排序容器元素](#64stdsort排序容器元素)\n\t\t- [6.5`std::transform`修改容器内容](#65stdtransform修改容器内容)\n\t\t- [6.6查找特定项](#66查找特定项)\n\t\t- [6.7将容器元素限制在`std::clamp`范围内](#67将容器元素限制在stdclamp范围内)\n\t\t- [6.8`std::sample`采集样本数据集](#68stdsample采集样本数据集)\n\t\t- [6.9生成有序数据序列](#69生成有序数据序列)\n\t\t- [6.10合并已排序容器](#610合并已排序容器)\n\t\t- [第六章总结](#第六章总结)\n\t- [第七章 字符串、流和格式化](#第七章-字符串流和格式化)\n\t\t- [7.3轻量字符串对象`std::string_view`](#73轻量字符串对象string_view)\n\t\t- [7.4连接字符串](#74连接字符串)\n\t\t- [7.5转换字符串](#75转换字符串)\n\t\t- [7.6使用格式库格式化文本](#76使用格式库格式化文本)\n\t\t- [7.7删除字符串的空白](#77删除字符串的空白)\n\t\t- [7.8从用户输入中读取字符串](#78从用户输入中读取字符串)\n\t\t- [7.9统计文件中的单词数](#79统计文件中的单词数)\n\t\t- [7.10使用文件输入初始化复杂结构体](#710使用文件输入初始化复杂结构体)\n\t\t- [7.11使用`char_traits`](#711使用char_traits)\n\t\t- [7.12用正则表达式解析字符串](#712用正则表达式解析字符串)\n\t\t- [第七章总结](#第七章总结)\n\t- [第八章 实用工具类](#第八章-实用工具类) \n    \t- [8.2`std::optional`管理可选值](#82-stdoptional-管理可选值)\n    \t- [8.3`std::any`保证类型安全](#83any保证类型安全) \n    \t- [8.4`std::variant`存储不同的类型](#84-stdvariant-存储不同的类型)\n    \t- [8.5`std::chrono`的时间事件](#85chrono的时间事件)\n    \t- [8.6对元组使用折叠表达式](#86对元组使用折叠表达式)\n    \t- [8.7`std::unique_ptr`管理已分配内存](#87-stdunique_ptr-管理已分配内存)\n    \t- [8.8`std::shared_ptr`的共享对象](#88-stdshared_ptr-的共享对象)\n    \t- [8.9对共享对象使用弱指针](#89对共享对象使用弱指针)\n    \t- [8.10共享管理对象的成员](#810共享管理对象的成员)\n    \t- [8.11比较随机数引擎](#811比较随机数引擎)\n    \t- [8.12比较随机数分布发生器](#812比较随机数分布发生器)\n    \t- [第九章总结](#第九章-并发和并行)\n    - [第九章 并发和并行](#第九章-并发和并行)\n        - [9.2休眠一定时间](#92休眠一定时间)\n        - [9.3`std::thread`实现并发](#93-stdthread-实现并发)\n        - [9.4`std::async`实现并发](#94-stdasync-实现并发)\n        - [9.5`STL`算法与执行策略](#95stl算法与执行策略)\n        - [9.6互斥锁和锁安全的共享数据](#96互斥锁和锁安全的共享数据)\n        - [9.7`std::atomic`共享标志和值](#97stdatomic共享标志和值)\n        - [9.8`std::call_once`初始化线程](#98stdcall_once初始化线程)\n        - [9.9`std::condition_variable`解决生产者-消费者问题](#99stdcondition_variable解决生产者-消费者问题)\n        - [9.10实现多个生产者和消费者](#910实现多个生产者和消费者)\n    - [第十章 文件系统](#第十章-文件系统)\n\t\t- [10.2为`path`类特化`formatter`](#102为path类特化formatter)\n\t\t- [10.3使用带有路径的操作函数](#103使用带有路径的操作函数)\n\t\t- [10.4列出目录中的文件](#104列出目录中的文件)\n\t\t- [10.5使用`grep`实用程序搜索目录和文件](#105使用grep实用程序搜索目录和文件)\n\t\t- [10.6使用`regex`和`directory_iterator`重命名文件](#106使用regex和directory_iterator重命名文件)\n\t\t- [10.7创建磁盘使用计数器](#107创建磁盘使用计数器)\n\n\n## 第一章 C++20的新特性\n### [1.2格式化文本](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.2%E6%A0%BC%E5%BC%8F%E5%8C%96%E7%89%B9%E5%8C%96formatter.cpp)\n\n ```cpp\n#include\u003ciostream\u003e\n#include\u003calgorithm\u003e\n#include\u003cstring_view\u003e\n#include\u003cformat\u003e\n\ntemplate \u003c typename... Args\u003e\nvoid print(const std::string_view fmt_str, Args\u0026\u0026... args) {\n\tauto fmt_args{ std::make_format_args(args...) };\n\tstd::string outstr{ std::vformat(fmt_str, fmt_args) };\n\tfputs(outstr.c_str(), stdout);\n}\n\nstruct Frac {\n\tint a, b;\n};\n\ntemplate\u003c\u003e\nstruct std::formatter\u003cFrac\u003e {\n\ttemplate\u003ctypename ParseContext\u003e\n\tconstexpr auto parse(ParseContext\u0026 ctx) {\n\t\treturn ctx.begin();\n\t}\n\ttemplate\u003ctypename FormatContext\u003e\n\tauto format(const Frac\u0026 f, FormatContext\u0026 ctx)const {\n\t\treturn std::format_to(ctx.out(), \"{0:d}/{1:d}\", f.a, f.b);\n\t}\n};\n\nint main() {\n\tFrac f{ 1,10 };\n\tprint(\"{}\", f);\n}\n//特化规则参见:\thttps://zh.cppreference.com/w/cpp/named_req/Formatter\n ```\n\n运行结果:\n\n\t1/10\n\n你可以把这个内容分为两个部分:\n1. 实现模板函数 **`print`**\n\n使用与`std::format()`函数相同的参数。第一个参数是格式字符串的 `std::string_view` 对象，后面作为参数 的可变参数包。\n\n[`std::make_format_args()`](https://zh.cppreference.com/w/cpp/utility/format/make_format_args) 函数的作用: *接受参数包并返回一个对象，该对象包含适合格式化的已擦除 类型的值。*\n\n`fmt_str`就是传递的格式化字符串，`fmt_args`是一个保有格式化参数的对象，使用[`std::vformat(fmt_str, fmt_args)`](https://zh.cppreference.com/w/cpp/utility/format/vformat)即可返回格式化完毕的字符串。我们使用 `fputs()` 将值输出到控制台上 (这比 `cout` 高效得多)。\n\n2. [**`std::formatter`**](https://zh.cppreference.com/w/cpp/utility/format/formatter) 特化\n\n对于自定义，或者说标准没有对其有特化的类型，需要我们自行特化`std::formatter`才可以正确的格式化。\n\n**`parse() `** 函数解析格式字符串，从冒号之后 (若没有冒号，则在开大括号之后) 直到但不包括结 束大括号 (就是指定对象类型的部分)。其接受一个 `ParseContext `对象，**并返回一个迭代器**。这里，可以只返回 `begin()` 迭代器。因为我们的类型不需要新语法，所以无需准备任何东西。 \n\n**`format()`** 函数接受一个 `Frac` 对象和一个 `FormatContext` 对象，**返回结束迭代器**。**`format_to()`** 函数可使这变得很容易，**其可以接受一个迭代器、一个格式字符串和一个参数包**。本例中，参数包是 Frac 类的两个属性，分子和分母。 需要做的就是提供一个简单的格式字符串`“{0}/{1}”`以及分子和分母的值 (0 和 1 表示参数的 位置)。\n\n\u003cbr\u003e\n\n### [1.3使用编译时constexpr `std::vector`和`std::string`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.3%E4%BD%BF%E7%94%A8%E7%BC%96%E8%AF%91%E6%97%B6constexpr%20vector%E5%92%8Cstring.cpp)\n```cpp\n#include\u003ciostream\u003e\n#include\u003cvector\u003e\nconstexpr auto f() {\n\tstd::vector\u003cint\u003ev{ 1,2,3 };\n\treturn v;\n}\nconstexpr auto f2() {\n\tint* p = new int{ 10 };\n\t//未delete解除分配\n\treturn *p;\n}\n\nint main() {\n\tconstexpr auto n = f().size();//√\n\t//constexpr auto n2 = f()//error\n\t//constexpr auto n3 = f2()//error\n}\n\n```\n\n`C++20` 允许在新的上下文中使用 [**`constexpr`**](https://zh.cppreference.com/w/cpp/language/constexpr)，这些语句可以在编译时计算，从而提高了效率(此关键字自`c++11`诞生，一直在增加和改进，我们不再强调)。\n\n\u003cbr\u003e\n\n其中包括在 `constexpr` 上下文中使用 [`string`](https://zh.cppreference.com/w/cpp/string/basic_string) 和 [`vector`](https://zh.cppreference.com/w/cpp/container/vector) 对象的能力。所以 **，这些对象本身可能不声 明为 constexpr**，**但可以在编译时上下文中使用。**\n\n```cpp\nconstexpr void f() {\n\tconstexpr std::string s{ \"乐\" };\n}//错误\nconstexpr void f() {\n\tstd::string s{ \"乐\" };\n}//正确\n```\n\n\u003cbr\u003e\n\n也可以在`constexpr`上下文中使用算法:\n\n```cpp\nconstexpr int use_vector() {\n\tstd::vector\u003cint\u003e vec{ 1, 2, 3, 4, 5 };\n\treturn accumulate(begin(vec), end(vec), 0);\n}\n\nint main() {\n\tconstexpr int ret = use_vector();\n}\n```\n\n\u003cbr\u003e\n\n`C++20` 开始，标准 `string` 和 `vector` 类具有`constexpr`限定的构造函数和析构函数，这是可在编译时使用的\n前提。所以，分配给 `string` 或 `vector` 对象的内存，也必须在编译时释放。\n\n例如，`constexpr` 函数返回一个 `vector`，编译时不会出错(但是实测 **`gcc msvc clang`** 全部编译错误):\n```cpp\nconstexpr auto f() {\n\tstd::vector\u003cint\u003ev{ 1,2,3 };\n\treturn v;\n}\n\nint main() {\n\tconstexpr auto ret = f();//error\n}\n```\n\n\u003cbr\u003e\n\n在编译期间分配和释放了 `vector` 对象，该对象在运行时不可用，理论上可以返回通过编译，实际不可，就算可，你返回了也没有\n办法去使用。在运行时使用一些 `vector` 对象的适配 `constexpr` 的方法，比如 `size()`，它是`constexpr`限定的。\n\n```cpp\nconstexpr auto f() {\n\tstd::vector\u003cint\u003ev{ 1,2,3 };\n\treturn v;\n}\nint main(){\n\tconstexpr auto n = f();//√\n}\n```\n\n\u003cbr\u003e\n\n### [1.4安全比较不同类型的整数`cmp_less`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.4%E5%AE%89%E5%85%A8%E6%AF%94%E8%BE%83%E4%B8%8D%E5%90%8C%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%95%B4%E6%95%B0cmp_less.cpp)\n\n``` cpp\n#include\u003ciostream\u003e\n\ntemplate\u003cclass T,class U\u003e\nconstexpr bool cmp_less(T t, U u)noexcept {\n\tusing UT = std::make_unsigned_t\u003cT\u003e;//有符号类型到无符号类型的安全转换。\n\tusing UU = std::make_unsigned_t\u003cU\u003e;\n\tif constexpr (std::is_signed_v \u003cT\u003e == std::is_signed_v\u003cU\u003e)\n\t\treturn t \u003c u;\n\telse if constexpr (std::is_signed_v\u003cT\u003e)\n\t\treturn t \u003c 0 ? true : UT(t) \u003c u;\n\telse\n\t\treturn u \u003c 0 ? false : t \u003c UU(u);\n}\nint main() {\n\tstd::cout \u003c\u003c std::boolalpha \u003c\u003c (5u \u003c -1) \u003c\u003c '\\n';//true\n\tstd::cout \u003c\u003c std::boolalpha \u003c\u003c ::cmp_less(5u, 1) \u003c\u003c '\\n';//false\n\tstd::cout \u003c\u003c std::boolalpha \u003c\u003c ::cmp_less(5u, 2u) \u003c\u003c '\\n';//false\n}\n\n```\n`C++20` 在 [**`utility`**](https://zh.cppreference.com/w/cpp/header/utility) 引入了一组[**比较函数**](https://zh.cppreference.com/w/cpp/utility/intcmp)，他们分别是：\n\n- `std::cmp_equal`\n- `std::cmp_not_equal`\n- `std::cmp_less`\n- `std::cmp_greater`\n- `std::cmp_less_equal`\n- `std::cmp_greater_equal`\n\n\u003cbr\u003e\n\n如上述[代码](https://zh.cppreference.com/w/cpp/utility/intcmp#.E5.8F.AF.E8.83.BD.E7.9A.84.E5.AE.9E.E7.8E.B0)中的例子一样，它与内建比较运算符不同，**负有符号整数**与**无符号整数**的比较结果始终为小于，且不为等于。\n\n\u003cbr\u003e\n\n### [1.5三路比较运算符](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.5%E4%B8%89%E8%B7%AF%E6%AF%94%E8%BE%83%E8%BF%90%E7%AE%97%E7%AC%A6.cpp)\n``` cpp\n#include\u003ciostream\u003e\n\nstruct X {\n\tint a{};\n\tdouble b{};\n\tchar c{};\n\tfriend auto operator\u003c=\u003e(const X\u0026,const X\u0026) = default;\n};\n\nstruct Y {\n\tint a = 6;\n};\n\nauto operator\u003c=\u003e(const Y\u0026 y, const Y\u0026 y2)noexcept-\u003eint {//自定义\n\tif (y.a == y2.a)return 0;\n\tif (y.a \u003e y2.a)return 1;\n\tif (y.a \u003c y2.a)return -1;\n}\nauto operator==(const Y\u0026 y, const Y\u0026 y2)noexcept-\u003ebool {\n\treturn y.a == y2.a;\n}\n\nint main() {\n\tX x{ 10,1.2,'*' };\n\tX x2{ 10,1,'*' };\n\tx == x2;\n\tx \u003c= x2;\n\tx != x2;\n\tx \u003e= x2;\n\tY y{ 1 };\n\tY y2{ 2 };\n\tstd::cout \u003c\u003c (y \u003c=\u003e y2) \u003c\u003c '\\n';\t\t\t\t//-1\n\tstd::cout \u003c\u003c std::boolalpha \u003c\u003c (y \u003e y2) \u003c\u003c '\\n';//false\n\tstd::cout \u003c\u003c std::boolalpha \u003c\u003c (y \u003c y2) \u003c\u003c '\\n';//true\n\tstd::cout \u003c\u003c std::boolalpha \u003c\u003c (y != y2) \u003c\u003c '\\n';//true\n}\n\n//三路比较运算符: https://zh.cppreference.com/w/cpp/language/operator_comparison\n//默认比较: https://zh.cppreference.com/w/cpp/language/default_comparisons\n```\n\n三路比较运算符表达式的形式为`表达式1 \u003c=\u003e 表达式2`该表达式将返回一个对象\n\n若`表达式1 \u003c 表达式2`，则`(表达式1 \u003c=\u003e 表达式2) \u003c 0`\n\n若`表达式1 \u003e 表达式2`，则`(表达式1 \u003c=\u003e 表达式2) \u003e 0`\n\n若`表达式1 == 表达式2`，则`(表达式1 \u003c=\u003e 表达式2) == 0`\n\n每当`\u003c` `\u003e` `\u003c=` `\u003e=` `\u003c=\u003e`被比较且重载决议选择该重载时，`operator\u003c=\u003e`都会被调用\n\n若`operator\u003c=\u003e`是默认版本且`operator==`完全没有被声明，则`operator==`将隐式采用默认版本\n```cpp\nstruct C\n{\n    int num;\n    auto operator\u003c=\u003e(const C\u0026 c)const = default;\n};\n\nint main()\n{\n    C c1{1};\n    C c2{2};\n\n    std::cout \u003c\u003c std::boolalpha \u003c\u003c (c1 == c2) \u003c\u003c '\\n';//隐式调用默认版本\n}\n```\n\n\u003cbr\u003e\n\n### [1.6查找特性测试宏](https://github.com/13870517674/Cpp20-STL-Cookbook-src/blob/master/src/1.6%E6%9F%A5%E6%89%BE%E7%89%B9%E6%80%A7%E6%B5%8B%E8%AF%95%E5%AE%8F.cpp)\n\n``` cpp\n#include\u003ciostream\u003e\n#include\u003cversion\u003e\n\n#ifdef __cpp_lib_three_way_comparison\n# include\u003ccompare\u003e\n#else\n# error 没有与之对应的头文件\n#endif // __cpp_lib_three_way_comparison\n\n#ifdef __cpp_lib_format\n# include\u003cformat\u003e\n#else\n# error 没有与之对应的头文件\n#endif // __cpp_lib_three_way_comparison\n\n#if __has_include(\u003ciostream\u003e)//检查能不能找到这个文件，如果能找到这个宏就返回1\n#  include \u003ciostream\u003e\n#endif\n\nint main() {\n\tstd::cout \u003c\u003c __cpp_lib_three_way_comparison \u003c\u003c '\\n';//为 201907，意味着其在 2019 年 7 月采纳。\n\tstd::cout \u003c\u003c __cpp_lib_format \u003c\u003c '\\n'; //为 202110，意味着其在 2021 年 10 月采纳。\n}\n\n//库功能性测试宏: https://zh.cppreference.com/w/cpp/utility/feature_test\n//诊断指令: https://zh.cppreference.com/w/cpp/preprocessor/error\n```\n\n运行结果：\n\n```\n201907\n202207\n```\n\n除此外，这里我跳转到MSVC上的[`version`](https://en.cppreference.com/w/cpp/header/version)库，我们还可以看到更多的版本宏\n\n```cpp\n#define __cpp_lib_jthread                 201911L\n#define __cpp_lib_latch                   201907L\n#define __cpp_lib_list_remove_return_type 201806L\n#define __cpp_lib_math_constants          201907L\n\n#ifdef __cpp_lib_concepts\n#define __cpp_lib_move_iterator_concept 202207L\n#endif // __cpp_lib_concepts\n\n#define __cpp_lib_polymorphic_allocator   201902L\n#define __cpp_lib_remove_cvref            201711L\n#define __cpp_lib_semaphore               201907L\n#define __cpp_lib_smart_ptr_for_overwrite 202002L\n\n#ifdef __cpp_consteval\n#define __cpp_lib_source_location 201907L\n#endif // __cpp_consteval\n\n#define __cpp_lib_span             202002L\n#define __cpp_lib_ssize            201902L\n#define __cpp_lib_starts_ends_with 201711L\n#define __cpp_lib_syncbuf          201803L\n\n#ifdef __cpp_lib_concepts\n#define __cpp_lib_three_way_comparison 201907L\n#endif // __cpp_lib_concepts\n\n#define __cpp_lib_to_address    201711L\n#define __cpp_lib_to_array      201907L\n#define __cpp_lib_type_identity 201806L\n#define __cpp_lib_unwrap_ref    201811L\n#endif // _HAS_CXX20\n```\n\n当然，你也可以到[`库特性测试宏 (C++20)`](https://zh.cppreference.com/w/cpp/utility/feature_test)来进行查看\n\n通常，我们使用条件宏增强不同平台下的兼容性：\n```cpp\n#if defined(__APPLE__)\n#define PI 3.14159265358979323846\n#elif defined(__linux__)\n#define PI 3.141592653589793\n#elif defined(_WIN32)\n#define PI 3.1415926535897932384626433832795\n#endif\n\n#ifdef _WIN32\n#include \u003cwindows.h\u003e\n#define sleep_ms(x) Sleep(x)\n#else\n#include \u003cchrono\u003e\n#include \u003cthread\u003e\n#define sleep_ms(x) std::this_thread::sleep_for(std::chrono::milliseconds(x))\n#endif\n```\n\n[`条件宏`](https://zh.cppreference.com/w/cpp/preprocessor/conditional)至C++23已经有了8种,分别是：\n\n```cpp\n#if\n#ifdef\n#ifndef\n#elif\n#elifdef  // C++23起\n#elifndef // C++23起\n#else\n#endif\n```\n\n\u003cbr\u003e\n\n### [1.7概念(`concept`)和约束(`constraint`)-创建更安全的模板](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.7%E6%A6%82%E5%BF%B5(concept)%E5%92%8C%E7%BA%A6%E6%9D%9F(constraint)-%E5%88%9B%E5%BB%BA%E6%9B%B4%E5%AE%89%E5%85%A8%E7%9A%84%E6%A8%A1%E6%9D%BF.cpp)\n``` cpp\n#include\u003ciostream\u003e\n\ntemplate\u003cstd::integral T\u003e\nvoid f(T t) {}\n\ntemplate\u003cclass T\u003e\nrequires std::integral\u003cT\u003e || std::is_pointer_v\u003cT\u003e\nstruct X {};\n\ntemplate \u003cclass T\u003e\nrequires std::is_integral_v\u003cT\u003e\nT n{};\n\ntemplate \u003cclass T\u003e\nconcept love = std::is_integral_v\u003cT\u003e \u0026\u0026 (std::is_same_v\u003cint, T\u003e || std::is_same_v\u003cuint32_t, T\u003e);\n\nvoid f2(love auto){}\n\nint main() {\n\tf(1);            // 1 是 int，约束满足\n\tf('*');          // '*' 是整数类型(Integer Type)之一，约束满足\n\t//f(1.2);\n\tX\u003cint\u003e x;        // int 满足两个约束的析取之一：std::integral\u003cT\u003e，约束满足\n\t//X\u003cdouble\u003ex2;\n\tX\u003cdouble*\u003e x3;   // double* 满足两个约束的析取之一：std::is_pointer_t\u003cT\u003e，约束满足\n\tn\u003cint\u003e = 3;\n\t//n\u003cdouble\u003e;\n\tstd::cout \u003c\u003c n\u003cint\u003e \u003c\u003c '\\n';\n\tf2(1);           // 满足合取 std::is_integral_v\u003cT\u003e 和 std::is_same_v\u003cint, T\u003e\n\tf2(1u);          // 满足合取 std::is_integral_v\u003cT\u003e，std::is_same_v\u003cuint32_t, T\u003e\n\t//f2(1l);\n}\n\n//Requires表达式 https://zh.cppreference.com/w/cpp/language/requires\n//约束与概念 https://zh.cppreference.com/w/cpp/language/constraints\n```\n\u003e*约束(Constraint)是对模板形参提出的一种要求，这种要求的具名集合被称为概念(Concept)。每一个概念均为[**谓词**](https://zh.cppreference.com/w/cpp/named_req/Predicate) ，且在**编译期求值**，并在用作约束时成为模板接口的一部分*。\n\n作为  `C++20` 引入的四大新特性之一：`Concept` ，提出了一种比 *SFINAE* 更好的约束方法，它易于理解和编写，也能在出现问题时给出更可读的编译期报错。概念的定义形式如下：\n\n\u003e **_template_** \u003c \u003cfont color=grey\u003e\u003ci\u003e模板形参列表\u003c/i\u003e\u003c/font\u003e \u003e\n\u003e **_concept_** \u003cfont color=grey\u003e\u003ci\u003e概念名\u003c/i\u003e\u003c/font\u003e \u003cfont color=grey\u003e\u003ci\u003e属性\u003c/i\u003e\u003c/font\u003e\u003cfont color=green\u003e(可选)\u003c/font\u003e **=** \u003cfont color=grey\u003e\u003ci\u003e约束表达式\u003c/i\u003e\u003c/font\u003e **;**\n\n在上述例子中，概念 `love` 的定义就是这样：\n```cpp\ntemplate \u003cclass T\u003e\nconcept love = std::is_integral_v\u003cT\u003e \u0026\u0026 (std::is_same_v\u003cint, T\u003e || std::is_same_v\u003cuint32_t, T\u003e);\n```\n\n其中，运算符 `\u0026\u0026` 构成两个约束的合取(*Conjunction*)，两个约束均满足时合取满足。`||` 构成的析取(*Disjunction*)则为两者之一满足则析取满足。\n\u003cbr\u003e\n\u003cbr\u003e\n`requires` 关键字可用于进行多个约束的分开表达，约束之间的关系均为合取，分为以下多种情况：\n\n- 简单约束\n```cpp\n// 1. 简单约束\ntemplate \u003ctypename T\u003e\nconcept Addable = requires(T a, T b)\n{\n\ta + b;    //编译器会检查该表达式是否 \"合法\"\n}\n```\n\n- 类型约束\n```cpp\ntemplate \u003ctypename T\u003e\nstruct tmp\n{\n\tusing value = T;\n};\n\ntemplate \u003ctypename T, typename = std::enable_if_t\u003cstd::is_same_v\u003cT, V\u003e\u003e\nstruct test {};\n\ntemplate \u003ctypename T\u003e\nusing Ref = T\u0026;\n\ntemplate \u003ctypename T\u003e\nconcept Cpt = requires\n{\n\ttypename T::value;    // 检查 T 是否存在成员 T::value\n\ttypename X\u003cT\u003e;        // 检查是否存在模板类 S 的特化 S\u003cT\u003e\n\ttypename Ref\u003cT\u003e;      // 检查是否存在合法别名模板 Ref\u003cT\u003e\n}\n```\n\n- 复合约束\n复合约束用于约束表达式的返回类型。其定义为：\n\n\u003e { \u003cfont color=grey\u003e\u003ci\u003e表达式\u003c/i\u003e\u003c/font\u003e } **noexcept**\u003cfont color=green\u003e(可选)\u003c/font\u003e -\u003e \u003cfont color=grey\u003e\u003ci\u003e类型约束\u003c/i\u003e\u003c/font\u003e **;**\n\n例如：\n```cpp\ntemplate \u003ctypename T\u003e\nconcept C = requires(T x) {\n  {x * 2} -\u003e typename T::inner;    // 表达式 x * 2 的类型可转换为 T::inner\n  {x + 3} -\u003e std::same_as\u003cint\u003e;    // 表达式 x + 3 需要满足约束 std::same_as\u003cint\u003e\n};\n```\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n复合约束的计算顺序为：\n- 计算 \u003cfont color=grey\u003e\u003ci\u003e表达式\u003c/i\u003e\u003c/font\u003e 是否合法\n- 如果有 **noexcept** 限定，则 \u003cfont color=grey\u003e\u003ci\u003e表达式\u003c/i\u003e\u003c/font\u003e 不能抛出异常\n- `decltype((expression))` 的类型必须满足 \u003cfont color=grey\u003e\u003ci\u003e类型约束\u003c/i\u003e\u003c/font\u003e\n全满足则结果为 `true` ，否则为 `false`\n\n\u003cbr\u003e\n\n### [1.8模块](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.8%E6%A8%A1%E5%9D%97.cpp)\n\n`1.8模块.cpp`\n\n``` cpp\nimport test;\n\nint main() {\n\t/*int n[]{\n#include\"t.txt\"\n\t};\n\tfor (auto i : n) {\n\t\tstd::cout \u003c\u003c i \u003c\u003c ' ';\n\t}*/\n\n\tstd::cout \u003c\u003c mylib::add(1, 2) \u003c\u003c '\\n';\n\t//mylib::print(\"*\");\n\tt();\n}\n\n//模块: https://zh.cppreference.com/w/cpp/language/modules\n//编译设置:add_executable (Test1 \"src/1.8模块.cpp\" \"src/test.ixx\" \"src/test2.ixx\")\n```\n\n[`test.ixx`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/test.ixx)\n\n```cpp\nmodule;\n#define PI 3.14\n\nexport module test;\nexport import\u003ciostream\u003e;\nexport import test2;\n\nnamespace mylib {\n\n\texport auto add(std::integral auto a, std::integral auto b) {\n\t\treturn a + b;\n\t}\n\n\tauto print(auto t) {\n\t\tstd::cout \u003c\u003c t \u003c\u003c '\\n';\n\t}\n}\n```\n\n[`test2.ixx`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/test2.ixx)\n\n```cpp\nexport module test2;\nimport\u003ciostream\u003e;\n\nexport void t() {\n\tstd::cout \u003c\u003c \"乐\\n\";\n}\n```\n\n[`t.txt`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/t.txt)\n\n```\n1,2,3,4,5\n```\n\n\n\n### [1.9视图](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.9%E8%A7%86%E5%9B%BE.cpp)\n\n``` cpp\n#include\u003ciostream\u003e\n#include\u003cranges\u003e\n#include\u003cvector\u003e\nnamespace stdv = std::views;\nnamespace stdr = std::ranges;\n\nvoid print(stdr::range auto v) {\n\tfor (const auto\u0026 i : v)std::cout \u003c\u003c i \u003c\u003c ' ';\n\tendl(std::cout);\n}\n\nint main() {\n\tstd::vector nums{ 1,2,3,4,5,6,7,8,9,10 };\n\tauto ret = nums | stdv::take(5) | stdv::reverse;\n\tprint(ret);\n\tauto ret2 = nums | stdv::filter([](int i) {return i \u003e 6; });\n\tprint(ret2);\n\tauto ret3 = nums | stdv::transform([](int i) {return i * i; });\n\tprint(ret3);\n\tprint(nums);//视图是不会改变原来的数据的\n\n\tstd::vector\u003cstd::string\u003estrs{ \"🐴\",\"🐭\",\"🥵\",\"🤣\" };\n\tauto ret4 = strs | stdv::reverse;\n\tprint(ret4);\n\n\tauto ret5 = nums | stdv::filter([](int i) {return i % 2 != 0; }) | stdv::transform([](int i) {return i * 2; });\n\tprint(ret5);\n\t\n\tauto nums_ = stdv::iota(1, 10);\n\tprint(nums_);\n\n\tauto rnums = stdv::iota(1) | stdv::take(200);\n\tprint(rnums);\n\n\tstdr::copy(strs | stdv::reverse | stdv::drop(2), std::ostream_iterator\u003cstd::string\u003e(std::cout,\" \"));\n}\n\n//范围库: https://zh.cppreference.com/w/cpp/ranges\n```\n\n### 第一章总结\n第一章的内容需要细看，很多其实书说的并不全面，比如范围，模块，约束与概念，自己注意去看我们提到的之前讲过的视频，以及这些demo\n如果你是初学，最好都自己写一下运行，顺便提一下`print.h`后面我们会经常用到这个头文件，我们其实是一步步补充的，但是我们直接把\n这个头文件的内容先放出来吧，有不少的打印模板函数\n\n**`print.h`**\n```cpp\n#pragma once\n#include\u003cformat\u003e\n#include\u003ciostream\u003e\n#include\u003cranges\u003e\n#include\u003cmap\u003e\n\ntemplate \u003c typename... Args\u003e\nvoid print(const std::string_view fmt_str, const Args\u0026... args) {\n\tauto fmt_args{ std::make_format_args(args...) };\n\tstd::string outstr{ std::vformat(fmt_str, fmt_args) };\n\tfputs(outstr.c_str(), stdout);\n}\n\nvoid print(std::ranges::range auto v){\n\tprint(\"size: {}  \", v.size());\n\tprint(\"[ \");\n\tfor (const auto\u0026 i : v)print(\"{} \", i);\n\tprint(\"]\\n\");\n}\n\ntemplate\u003cclass T,class T2\u003e\nvoid print(const std::map\u003cT, T2\u003e\u0026 map) {\n\tprint(\"size: {} \", map.size());\n\tprint(\"[ \");\n\tfor (auto\u0026 [k, v] : map)print(\"{}:{} \", k, v);\n\tprint(\"]\\n\");\n}\n\ntemplate\u003cclass T, class T2\u003e\nvoid rprint(std::multimap\u003cT, T2\u003e\u0026 todo) {\n\tfor (const auto\u0026 i : todo | std::views::reverse) {\n\t\tprint(\"{}: {}\\n\", i.first, i.second);\n\t}\n\tprint(\"\\n\");\n}\n\nvoid printc(const std::ranges::range auto\u0026 v, std::string_view s = \"\") {\n\tif (s.size())print(\"{}: \", s);\n\tfor (const auto\u0026 i : v)print(\"[{}] \", i);\n\tprint(\"\\n\");\n}\n\nvoid printr(const auto\u0026 r, std::string_view s = \"\") {\n\tauto rbegin = std::rbegin(r);\n\tauto rend = std::rend(r);\n\tfor (auto it = rbegin; it != rend; ++it) {\n\t\tprint(\"{} \", *it);\n\t}\n\tprint(\"\\n\");\n}\n```\n\n\u003cbr\u003e\n\n---\n## 第二章 STL的泛型特性\n### [2.2span类](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/2.2span%E7%B1%BB.cpp)\n\n```cpp\n#include \u003ciostream\u003e\n#include \u003cformat\u003e\n#include \u003cspan\u003e\n#include \u003cvector\u003e\n#include \u003carray\u003e\n\ntemplate \u003c typename... Args\u003e\nvoid print(const std::string_view fmt_str, Args\u0026\u0026... args) {\n\tauto fmt_args{ std::make_format_args(args...) };\n\tstd::string outstr{ std::vformat(fmt_str, fmt_args) };\n\tfputs(outstr.c_str(), stdout);\n}\n\ntemplate\u003cclass T\u003e\nvoid pspan(std::span\u003cT\u003e s) {\n\tprint(\"number of elemnts: {}\\n\", s.size());//  返回序列中的元素个数\n\tprint(\"size of span: {}\\n\", s.size_bytes());// 返回以字节表示的序列大小\n\tfor (auto i : s) print(\"{} \", i);\n\tendl(std::cout);\n}\n\nint main() {\n\tint a[]{ 1, 2, 3, 4, 5, 6 };\n\tpspan\u003cint\u003e(a);\n\n\tstd::endl(std::cout);\n\tstd::vector\u003cint\u003e b{1, 2, 3, 4, 5 };\n\tpspan\u003cint\u003e(b);\n\n\tstd::endl(std::cout);\n\tstd::array\u003cint, 4\u003e c{ 1, 2, 3, 4 };\n\tpspan\u003cint\u003e(c);\n}\n\n```\n\n运行结果:\n\n```cpp\nnumber of elemnts: 6\nsize of span: 24\n1 2 3 4 5 6\n```\n\n[**`std::span`**](https://zh.cppreference.com/w/cpp/container/span) 在C++20中被引入\n\n它给具有连续对象的序列提供了轻量级的视图，以更加安全的方式对其进行迭代和索引，比如`std::array`、 `std::vector`、原生数组和原生指针。\n\n常用于去包裹原生数组，并提供了更加安全的一系列函数：如`front()`，`begin()`, `size()`, `empty()`等\n\n经典的实现中只有两个成员：\n\n```cpp\nprivate:\n\tpointer _ptr;//指向元素的指针\n\tstd::size_t _size;//元素个数\n```\n\n\u003cbr\u003e\n\n### [2.3结构化绑定](https://github.com/13870517674/Cpp20-STL-Cookbook-src/blob/master/src/2.3%E7%BB%93%E6%9E%84%E5%8C%96%E7%BB%91%E5%AE%9A.cpp)\n\n```cpp\n#include\u003ciostream\u003e\n#include\u003cformat\u003e\n#include\u003carray\u003e\n#include\u003ctuple\u003e\n#include\u003cmap\u003e\n\ntemplate \u003c typename... Args\u003e\nvoid print(const std::string_view fmt_str, Args\u0026\u0026... args) {\n\tauto fmt_args{ std::make_format_args(args...) };\n\tstd::string outstr{ std::vformat(fmt_str, fmt_args) };\n\tfputs(outstr.c_str(), stdout);\n}\n\nstruct X { int a; double b; std::string str; };\n\nauto f() -\u003e std::tuple\u003cint, int\u003e {\n\treturn { 1,2 };\n}\n\nint main() {\n\tint array[]{ 1,2,3,4,5 };\n\tstd::cout \u003c\u003c (*(\u0026array[1])) \u003c\u003c std::endl;\n\tauto\u0026 [a, b, c, d, e] = array;// a 是 array[0] 的引用, b 是 array[1] 的引用 ...\n\tprint(\"{} {} {} {} {}\\n\", a, b, c, d, e);\n\ta = 10;\n\tprint(\"{}\\n\", array[0]);\n\n\tstd::array arr{ '*','a','b','\u0026' };\n\tauto [a_, b_, c_, d_] = arr;// a_ 是 arr.at(0) 的值拷贝, b_ 是 arr.at(1) 的值拷贝 ...\n\tprint(\"{} {} {} {}\\n\", a_, b_, c_, d_);\n\n\tstd::tuple\u003cint, double, std::string\u003etu{ 10,3.14,\"🥵\" };\n\tauto [t1, t2, t3] = tu;// 对 tuple 成员进行绑定，值拷贝\n\tprint(\"{} {} {}\\n\", t1, t2, t3);\n\n\tX x{ 1,5.2,\"🤣\" };\n\tauto [x1, x2, x3] = x;// 对 结构体 数据成员按声明顺序进行绑定，值拷贝\n\tprint(\"{} {} {}\\n\", x1, x2, x3);\n\n\tconst std::array arr2{ 1,2 };\n\tauto\u0026 [c_arr1, c_arr2] = arr2;\n\t//c_arr1 = 10;//error 常量引用不允许修改\n\n\tauto [f1, f2] = f();\n\tprint(\"{} {}\\n\", f1, f2);\n\n\tstd::map\u003cint, std::string\u003eMap{ {1,\"*\"},{2,\"😘\"} };\n\tfor (const auto\u0026 [m_a, m_b] : Map) {// 对 pair 进行绑定\n\t\tprint(\"{} {}\\n\", m_a, m_b);\n\t}\n}\n```\n\n运行结果:\n\n\t1 2 3 4 5\n\t10\n\t* a b \u0026\n\t10 3.14 🥵\n\t1 5.2 🤣\n\t1 2\n\t1 *\n\t2 😘\n\n注意，由于结构化绑定使用自动类型推导，所以类型声明必须使用 `auto`,且使用的变量名在该作用域内唯一，同时保证标识符列表内的标识符（即[a, b, c] 中的变量a,b,c）个数**等于**所指代对象的子元素个数\n\n[Lambda表达式(C++11 起) ](https://zh.cppreference.com/w/cpp/language/lambda)在C++17起才允许捕获结构化绑定的变量\n\n```cpp\nstruct S { int p{6}, q{7}; };\nconst auto\u0026 [b, d] = S{};\nauto l = [b, d] { return b * d; }; // C++17 起合法\nassert(l() == 42);\n```\n\n\u003cbr\u003e\n\n### [2.4`if`\u0026`switch`中的初始化](https://github.com/13870517674/Cpp20-STL-Cookbook-src/blob/master/src/2.4if%26switch%E4%B8%AD%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96.cpp)\n\n```cpp\n#include\u003cmutex\u003e\n#include\u003cthread\u003e\n#include\u003cformat\u003e\n\ntemplate \u003c typename... Args\u003e\nvoid print(const std::string_view fmt_str, Args\u0026\u0026... args) {\n\tauto fmt_args{ std::make_format_args(args...) };\n\tstd::string outstr{ std::vformat(fmt_str, fmt_args) };\n\tfputs(outstr.c_str(), stdout);\n}\n\nstd::mutex mtx;\nbool flag = true;\n\nvoid ifFunc(int n) {\n\tif (auto flag = [n]() {return n; }(); flag % 2 == 0) {// C++17起，允许if语句内声明表达式，它可以是这里的lambda表达式\n\t\tprint(\"This is a even Number: {}\\n\", n);\n\t}\n}\n\nvoid f(int n) {\n\tif (std::lock_guard lg{ mtx }; flag) {\n\t\tprint(\"if_start\\t\");\n\t\tprint(\"{}\\t\", n);\n\t\tprint(\"end\\n\");\n\t}\n}\n\n\nvoid switchFunc() {\n\tswitch (char c = getchar(); c)// C++17起，允许switch语句内声明表达式，它可以是一条语句\n\t{\n\tcase 'a':\n\t\tprint(\"a\\n\");\n\t\tbreak;\n\tcase 'b':\n\t\tprint(\"b\\n\");\n\t\tbreak;\n\tcase 'c':\n\t\tprint(\"c\\n\");\n\t\tbreak;\n\tdefault:\n\t\tprint(\"input not a b c\\n\");\n\t\tbreak;\n\t}\n}\n\nint main() {\n\tfor (int i = 0; i \u003c 5; i++) {\n\t\tstd::jthread t{ f,i };\n\t\tstd::jthread t2{ f,i };\n\t}\n\tifFunc(3);\n\tswitchFunc();\n}\n```\n\n输入：a\n\n可能的运行结果：\n\n```\nif_start        0       end\nif_start        0       end\nif_start        1       end\nif_start        1       end\nif_start        2       end\nif_start        2       end\nif_start        3       end\nif_start        3       end\nif_start        4       end\nif_start        4       end\na\n```\n\n初始化语句可以是任意**一条**语句，如上面代码中的`lambda语句`，也可以是一条简单声明`int a = 3, b = 3;` 或者是一条结构化绑定的声明，C++23起将支持[`别名声明(C++11起)`](https://zh.cppreference.com/w/cpp/language/type_alias)\n\n通过`if \u0026 switcht 初始化语句`限制了变量的作用域，避免了与其他变量名发生冲突，并且会自动调用对应的析构函数，确保内存被安全释放（比如上面代码中的[std::lock_guard](https://zh.cppreference.com/w/cpp/thread/lock_guard)）\n\n### [2.5模板参数推导（CTAD）](https://github.com/13870517674/Cpp20-STL-Cookbook-src/blob/master/src/2.5%E6%A8%A1%E6%9D%BF%E5%8F%82%E6%95%B0%E6%8E%A8%E5%AF%BC.cpp)\n\n```cpp\n#include\"print.h\"\n\nusing namespace std::string_literals;\n\ntemplate\u003cclass T\u003e\nstruct X {\n\tT v{};\n\ttemplate\u003cclass...Args\u003e\n\tX(Args\u0026\u0026...args) : v{ (args + ...) } {}\n};\n\ntemplate\u003cclass...Ts\u003e\nX(Ts...ts) -\u003e X\u003cstd::common_type_t\u003cTs...\u003e\u003e;//确定所有类型Ts...都能隐式转换到的类型\n\nint main() {\n\tX x(\"Hello \", \"World🤣\"s);\n\tprint(\"{}\\n\", x.v);\n}\n```\n\n运行结果：\n\n```cpp\nHello World🤣\n```\n\n在C++17,当我们给定类模板实参时，编译器会对其进行自动类型推导，如上面代码代码中的实例化对象`x`,  而之前为了实现`x对象的实例化，我们可能需要这样写：\n\n```cpp\nX\u003cconst char*, std::string\u003e x(\"Hello\", \"World\"s);\n```\n\n虽然有了`类模板实参推导`,但该类模板只接收一种类型，所以需要使用[`std::common_type_t`](https://zh.cppreference.com/w/cpp/types/common_type)来对类模板实参进行一个都可隐式转换的类型的提取\n\n因此，当我们初始化STL容器时，可以省略类型的书写：\n\n```cpp\nstd::pair p{ 2, 3.14 };// 省略容器元素的类型\nstd:vector vec{ 1, 2, 3, 4 };\nstd::sort(vec.begin(), vec.end(), std::greater\u003c\u003e());//省略比较器的类型\n```\n\n### [2.6编译期`if`](https://github.com/13870517674/Cpp20-STL-Cookbook-src/blob/master/src/2.6%E7%BC%96%E8%AF%91%E6%9C%9Fif.cpp)\n\n```cpp\n#include\"print.h\"\n\ntemplate\u003cclass T\u003e\nauto f(const T\u0026 v) {\n\tif constexpr (std::is_pointer_v\u003cT\u003e)\n\t\tprint(\"is pointer\\n\");\n\telse\n\t\tprint(\"not pointer\\n\");\n}\n\ntemplate\u003cclass T,class...Args\u003e\nvoid show(T t, Args\u0026\u0026...args) {\n\tprint(\"{}\\t\",t);\n\tif constexpr (sizeof...(args)) {\n\t\tshow(args...);\n\t}\n}\n\nint main() {\n\tint* p{};\n\tf(p);\n\tf(1);\n\tshow(5,314, \"🤣\", '*');\n\tprint(\"\\n\");\n}\n```\n\n运行结果：\n\n```\nis pointer\nnot pointer\n5       314     🤣   *\n```\n\n[`std::is_pointer`](https://zh.cppreference.com/w/cpp/types/is_pointer)用于编译器判断参数类型T是否为对象/函数指针\n\n以 [`if constexpr`](https://zh.cppreference.com/w/cpp/language/if) 开始的语句被称为 *constexpr if 语句*, 在 *constexpr if* 语句中, 若表达式的值可转换到bool类型的常量表达式，如果值为`true`，舍弃`false`分支（如果存在），反之亦然\n\n被舍弃的分支中的`return 语句`**不参与**函数的返回值类型推导，且可以使用**未定义**的变量（大概是因为他不会被执行到，所以无关紧要）\n\n`sizeof...`在编译期求出参数包的大小，值为0时，被决为`false`\n\n### 第二章总结\n第二章内容总体比较简单，并没有什么困难的，重在运用，最好这些demo都自己抄或者写一遍。\n加深理解\n\u003cbr\u003e\n\n---\n## 第三章 STL容器\n### [3.3使用擦除函数从容器中擦除项](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.3%E4%BD%BF%E7%94%A8%E6%93%A6%E9%99%A4%E5%87%BD%E6%95%B0%E4%BB%8E%E5%AE%B9%E5%99%A8%E4%B8%AD%E6%93%A6%E9%99%A4%E9%A1%B9.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003clist\u003e\n\ntemplate\u003ctypename Tc,typename Tv\u003e\nvoid remove_value(Tc\u0026 c, const Tv\u0026 v) {//C++20之前的做法\n\tauto remove_it = std::remove(c.begin(), c.end(), v);//remove_it是首个需要被删除元素的位置\n\tc.erase(remove_it, c.end());//删除remove_it到end()这个范围的元素\n}\n\nint main() {\n\tstd::vector v{ 1,2,3,4,5 };\n\tprint(v);\n\t::remove_value(v, 1);\n\tprint(v);\n\tstd::erase(v,5);\n\tprint(v);\n\tstd::erase_if(v, [](int i) {return i % 2 != 0; });\n\tprint(v);\n\n\tstd::list list{ 1,2,3,4,5,6,7,8,9,10 };\n\tstd::erase(list, 5);\n\tstd::erase_if(list, [](int i) {return i % 2 == 0; });\n\tprint(list);\n\n\tstd::map\u003cint, std::string\u003e map{ {1,\"🤣\"},{2,\"🥵\"},{3,\"🐴\"},{4,\"🐭\"} };\n\tprint(map);\n\tstd::erase_if(map, [](auto\u0026 i) {\n\t\tconst auto\u0026 [k, v] = i;\n\t\treturn v == \"🥵\";\n\t});\n\tprint(map);\n}\n```\n**运行结果**   \n\n\t\tsize: 5  [ 1 2 3 4 5 ]\n\t\tsize: 4  [ 2 3 4 5 ]\n\t\tsize: 3  [ 2 3 4 ]\n\t\tsize: 2  [ 2 4 ]\n\t\tsize: 4  [ 1 3 7 9 ]\n\t\tsize: 4 [ 1:🤣 2:🥵 3:🐴 4:🐭 ]\n\t\tsize: 3 [ 1:🤣 3:🐴 4:🐭 ]\n\n\u003cbr\u003e\n\n**解析**\n\n[**`std::remove`**](https://zh.cppreference.com/w/cpp/io/c/remove)\n- 功能: 该函数用于将迭代器中与值匹配的元素移动到末尾,并返回操作完毕后首个与参数值匹配的元素位置   \n- 参数 `_First` 需要进行操作的容器的起始位置\n- 参数 `_Last`  需要进行操作的容器的截止位置\n- 参数 `_Val`\t需要操作的值   \n- Ps: `std::remove` 提供了可自定义操作规则的 [**`std::remove_if`**](https://zh.cppreference.com/w/cpp/algorithm/remove)\n\n\u003cbr\u003e\n\n[**`std::erase`**](https://zh.cppreference.com/w/cpp/container/vector/erase2)\n- 功能: 删除给定容器中与 `_Value` 匹配的元素\n- 参数 `_Cont` 需要被擦除元素的容器\n- 参数 `_Value` 需要被擦除的值   \n- Ps: 该函数从 `C++20` 起,功能同 remove_value()\n\n\u003cbr\u003e\n\n[**`std::erase_if`**](https://zh.cppreference.com/w/cpp/container/vector/erase2)\n- 功能: `std::erase` 的自定义删除规则版本\n- 参数 `_Cont` 需要被擦除元素的容器   \n- 参数 `_Pred` 当该参数为 `true` 时,擦除对应元素。该参数必须是一个可转换为 `bool` 类型的表达式(此处使用一个`lambda 表达式` 来判断是否擦除)\n- Ps: 该函数是 `std::erase` 的改进版本,相较于旧版本只能单一匹配值来进行删除,`std::erase_if`可以实现类似示例中的自定义删除规则\n\n**注意**: `std::erase` 与 `std::erase_if` 会使序列容器迭代器失效\n\n\u003cbr\u003e\n\n### [3.4常数时间内从未排序的向量中删除项](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.4%E5%B8%B8%E6%95%B0%E6%97%B6%E9%97%B4%E5%86%85%E4%BB%8E%E6%9C%AA%E6%8E%92%E5%BA%8F%E7%9A%84%E5%90%91%E9%87%8F%E4%B8%AD%E5%88%A0%E9%99%A4%E9%A1%B9.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003cranges\u003e\nnamespace stdr = std::ranges;\n\n//使用下标的版本\ntemplate\u003ctypename T\u003e\nvoid quick_delete(T\u0026 v, size_t idx) {\n\tif (idx \u003c v.size()) {\n\t\tv[idx] = std::move(v.back());\n\t\tv.pop_back();\n\t}\n}\n//使用迭代器的版本\ntemplate\u003ctypename T\u003e\nvoid quick_delete(T\u0026 v, typename T::iterator it) {\n\tif (it \u003c v.end()) {\n\t\t*it = std::move(v.back());\n\t\tv.pop_back();\n\t}\n}\n//若 vector 中项目的顺序不重要，就可以优化这个过程，使其花费 O(1)(常数) 时间\n//做法很简单，将传入的要删除的迭代器或索引赋值为末尾元素的值，然后将末尾元素删除，就完成了，但是没有顺序\n\nint main() {\n\tstd::vector v{ 1,2,3,4,5 };\n\tprint(v);\n\tauto it = stdr::find(v, 3);\n\tquick_delete(v, it);\n\tprint(v);//顺序不对，正常现象\n\n\tquick_delete(v, 2);\n\tprint(v);\n}\n```\n\n### [3.5安全的访问`std::vector`元素](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.5%E5%AE%89%E5%85%A8%E7%9A%84%E8%AE%BF%E9%97%AEvector%E5%85%83%E7%B4%A0.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n\nvoid test1() {\n\tstd::vector v{ 1,2,3,4,5 };\n\tv[5] = 2001;//写入非法内存，访问也是越界\n\tauto\u0026 i = v[5];//引用了错误的内存\n\tprint(\"{}\\n\", i);//可能发生错误，不保证\n}\n\nvoid test2()try {\n\tstd::vector v{ 1,2,3,4,5 };\n\tauto\u0026 i = v.at(5);// at会进行越界检查，保证了程序的安全\n\tprint(\"{}\\n\", i);\n}\ncatch (std::exception\u0026 e) {\n\tprint(\"{}\\n\", e.what());\n}\n\nvoid test3()try {\n\tstd::vector v{ 1,2,3,4,5 };\n\tauto\u0026 i = v[5];\n\tprint(\"{}\\n\", i);\n}\ncatch (std::exception\u0026 e) {\n\tprint(\"{}\\n\", e.what());\n}\nint main() {\n\t//test1();//error\n\ttest2();\n\t//test3();//error\n}\n```\n\n### [3.6保持`std::vector`元素的顺序](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.6%E4%BF%9D%E6%8C%81vector%E5%85%83%E7%B4%A0%E7%9A%84%E9%A1%BA%E5%BA%8F.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cstring\u003e\n#include\u003cvector\u003e\n#include\u003clist\u003e\nusing Vstr = std::vector\u003cstd::string\u003e;\nnamespace stdr = std::ranges;\n\nvoid psorted(stdr::range auto\u0026\u0026 v) {\n\tif  (stdr::is_sorted(v))\n\t\tprint(\"sorted: \");\n\telse\n\t\tprint(\"unsorted: \");\n\tprint(v);\n}\n\nvoid insert_sorted(Vstr\u0026 v, const std::string\u0026 s) {\n\t//lower_bound() 算法查找不小于实参的第一个元素的迭代器\n\tconst auto pos{ stdr::lower_bound(v,s) };\n\tv.insert(pos, s);//使用 lower_bound() 返回的迭代器在正确的位置插入一个元素\n}\n\ntemplate\u003cstdr::range C,typename E\u003e\nvoid insert_sorted(C\u0026 c, const E\u0026 e) {\n\tconst auto pos{ stdr::lower_bound(c,e) };\n\tc.insert(pos, e);\n}\n\nint main() {\n\tstd::vector\u003cstd::string\u003e v{ \"2\",\"1\",\"3\"};\n\tpsorted(v);//无序\n\n\tstdr::sort(v);\n\tpsorted(v);//有序\n\n\t//v.emplace_back(\"0\");\n\t//psorted(v);//无序\n\n\t::insert_sorted(v, \"0\");\n\tpsorted(v);//有序，相比于普通插入的优势\n\t//用list测试改写泛型的版本\n\tstd::list\u003cint\u003elist{ 1,2,3,4,5 };\n\tpsorted(list);//有序\n\t::insert_sorted(list, 0);\n\tpsorted(list);//有序\n}\n```\n\n### [3.7高效的将元素插入到`std::map`中](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.7%E9%AB%98%E6%95%88%E7%9A%84%E5%B0%86%E5%85%83%E7%B4%A0%E6%8F%92%E5%85%A5%E5%88%B0map%E4%B8%AD.cpp)\n```cpp\n#include\"print.h\"\n\nstruct X {\n\tstd::string s;\n\tX() { print(\"default construct\\n\"); }\n\tX(const char* s) :s{ s } { print(\"construct\\n\"); }\n\tX(const X\u0026) { print(\"copy construct\\n\"); }\n};\nvoid printm(const std::map\u003cint, X\u003e\u0026 map) {\n\tfor (const auto\u0026 [k, v] : map) {\n\t\tprint(\"[ {}:{} ]\", k, v.s);\n\t}\n\tprint(\"\\n\");\n}\n\nint main() {\n\tstd::map\u003cint, X\u003emap{};\n\tmap[1] = \"🐴\";//两个构造的开销，有参和默认\n\tprint(\"\\n\");\n\t//直接转发，只有一个有参构造的开销,这里使用try_emplace和emplace效果完全一样\n\tmap.emplace(2,\"🥵\");\n\tmap.emplace(3, \"🤣\");\n\tprintm(map);\n\tprint(\"\\n\");\n\n\tmap.emplace(1, \"乐\");//添加一个具有重复键的元素\n\tmap.try_emplace(1, \"乐\");\n\tprintm(map);\n}\n//重复键元素的问题参见 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92300\n```\n\n### [3.8高效的修改`std::map`项的键值](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.8%E9%AB%98%E6%95%88%E7%9A%84%E4%BF%AE%E6%94%B9map%E9%A1%B9%E7%9A%84%E9%94%AE%E5%80%BC.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cstring\u003e\n\ntemplate\u003ctypename M,typename K\u003e\nbool node_swap(M\u0026 m, K k1, K k2) {\n\t//extract 是更换 map 的键而不重分配的唯一方式\n\tauto node1{ m.extract(k1) };\n\tauto node2{ m.extract(k2) };\n\tif (node1.empty() || node2.empty())\n\t\treturn false;\n\tstd::swap(node1.key(), node2.key());\n\tm.insert(std::move(node1));\n\tm.insert(std::move(node2));\n\treturn true;\n}\n\nint main() {\n\tstd::map\u003cuint32_t, std::string\u003emaps{\n\t\t{1,\"🐴\"},{2,\"🥵\"},{3,\"🤣\"},{4,\"🐭\"},{5,\"😘\"}\n\t};\n\tprint(maps);\n\t::node_swap(maps, 3, 5);\n\tprint(maps);\n\n\tauto node = maps.extract(maps.begin());\n\tnode.key() = 5;\n\tauto t =maps.insert(std::move(node));\n\tprint(maps);\n\tif (!t.inserted) {\n\t\tprint(\"插入失败 {}\\n\",t.position-\u003esecond);\n\t}\n}\n```\n\n### [3.9自定义键值的`std::unordered_map`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.9%E8%87%AA%E5%AE%9A%E4%B9%89%E9%94%AE%E5%80%BC%E7%9A%84unordered_map.cpp)\n\n```cpp\n#include\"print.h\"\n#include\u003cstring\u003e\n#include\u003cunordered_map\u003e\n\nstruct Coord {\n\tint x{};\n\tint y{};\n};\nauto operator==(const Coord\u0026 a, const Coord\u0026 b) {//键值需要能比较相等，即equal_to\u003c_KeyTpey\u003e\n\treturn a.x == b.x \u0026\u0026 a.y == b.y;\n}\nnamespace std {\n\ttemplate\u003c\u003e\n\tstruct hash\u003cCoord\u003e {//特化哈希类\n\t\tsize_t operator()(const Coord\u0026a)const {\n\t\t\treturn static_cast\u003csize_t\u003e(a.x) + static_cast\u003csize_t\u003e(a.y);\n\t\t}\n\t};\n}\ntemplate\u003cclass T, class T2\u003e\ninline void print(const std::unordered_map\u003cT, T2\u003e\u0026 map) {\n\tprint(\"size: {} \", map.size());\n\tfor (auto\u0026 [k, v] : map)print(\"{{{} {}}}:{} \", k.x, k.y, v);\n\tprint(\"\\n\");\n}\nint main() {\n\tstd::unordered_map\u003cCoord, std::string, std::hash\u003cCoord\u003e\u003emap{ {{1,1},\"😘\"},{{0,0},\"🤣\"} };\n\tprint(map);\n}\n```\n\n运行结果:\n\n```\nsize: 2 {1 1}:😘 {0, 0}:🤣\n```\n\n[unordered_map](https://zh.cppreference.com/w/cpp/container/unordered_map)和`map`一样都是关联式容器,唯一不同的是`unordered_mp`内部不以任何顺序进行排列，而是直接存进桶里面，所以在不需要排序的场景下，`unordered_mp`会比`map`高效一些。\n\n`unordered_map`的定义\n```cpp\ntemplate\u003c\n    class Key,\n    class T,\n    class Hash = std::hash\u003cKey\u003e,\n    class KeyEqual = std::equal_to\u003cKey\u003e,\n    class Allocator = std::allocator\u003c std::pair\u003cconst Key, T\u003e \u003e\n\u003e class unordered_map;\n```\n\n`equal_to`在MSVC上的实现\n```cpp\n_EXPORT_STD template \u003cclass _Ty = void\u003e\nstruct equal_to {\n    using _FIRST_ARGUMENT_TYPE_NAME _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS  = _Ty;\n    using _SECOND_ARGUMENT_TYPE_NAME _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS = _Ty;\n    using _RESULT_TYPE_NAME _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS          = bool;\n\n    _NODISCARD constexpr bool operator()(const _Ty\u0026 _Left, const _Ty\u0026 _Right) const\n        noexcept(noexcept(_Fake_copy_init\u003cbool\u003e(_Left == _Right))) /* strengthened */ {\n        return _Left == _Right;\n    }\n};\n```\n~~同样的，你也可以重载`!=`然后指定`KeyEqual = std::not_equal_to\u003cKey\u003e`~~\n\n### [3.10使用`set`进行输入和筛选](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.10%E4%BD%BF%E7%94%A8set%E8%BF%9B%E8%A1%8C%E8%BE%93%E5%85%A5%E5%92%8C%E7%AD%9B%E9%80%89.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cset\u003e\n#include\u003cstring\u003e\n#include\u003cranges\u003e\n\nint main() {\n\tstd::set\u003cstd::string\u003e sets;//set 容器用于存储键适用于索引关键字\n\n\tstd::copy(std::istream_iterator\u003cstd::string\u003e{std::cin}, {},\n\t\tstd::inserter(sets, sets.end()));\n\t\t\n\tprint(sets);\n}\n```\n[**``std::copy``**](https://zh.cppreference.com/w/cpp/algorithm/copy)  用于将数据拷贝到对应容器中\n- 参数1 `_First` 需要拷贝的起始迭代器(这里使用``istream``的迭代器来读取输入流字符串)   \n- 参数2 `_Last`  拷贝的截止迭代器 (这里使用 `{}` 占位 即拷贝所有输入流中的字符)   \n- 参数2 `_Dest`  如何拷贝(这里使用``std::inserter``进行插入)   \n \n[**``std::inserter``**](https://zh.cppreference.com/w/cpp/algorithm/copy)  将每一组输入的字符串作为 `key` 插入到容器中\n- 参数1 `_Cont`\t 需要插入数据的容器(这里是sets)   \n- 参数2 `_Where` 需要插入的位置(这里始终插入到`sets`的尾部)   \n\n**运行结果**\n\n\t\t输入:  1 12 3 3 3 3 3 ^Z\n\t\t输出:  size: 3 [ 1 12 3 ]\n \n`set` 容器的 `key` 是不可重复的,如果需要运行重复 `key` 的 `set` 可以使用 `std::multiset`   \n\n`set` 容器内部通过一颗 `R\u0026B树(红黑树)`来存储数据,其对字符串的排序方式是按照 [**字典序**](https://baike.baidu.com/item/%E5%AD%97%E5%85%B8%E5%BA%8F#:~:text=%E5%9C%A8%E6%95%B0%E5%AD%A6%E4%B8%AD%EF%BC%8C%E5%AD%97%E5%85%B8%E6%88%96,%E9%A1%BA%E5%BA%8F%E6%8E%92%E5%88%97%E7%9A%84%E6%96%B9%E6%B3%95%E3%80%82)故输出时 `12` 出现在 `3` 之前\n\n\u003cbr\u003e\n\n\n### [3.11实现简单的RPN计算器与`deque`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.11%E5%AE%9E%E7%8E%B0%E7%AE%80%E5%8D%95%E7%9A%84RPN%E8%AE%A1%E7%AE%97%E5%99%A8%E4%B8%8Edeque.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cdeque\u003e\n#include\u003cstring\u003e\n\nclass RPN {\n\tstd::deque\u003cdouble\u003edeq_{};\n\tconstexpr static double zero_{ 0.0 };\n\tconstexpr static double inf_{ std::numeric_limits\u003cdouble\u003e::infinity() };\n\n\tbool is_numeric(const std::string str) {\n\t\tfor (const auto\u0026 i:str) {\n\t\t\tif (i != '.' \u0026\u0026 !std::isdigit(i))\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tstd::pair\u003cdouble, double\u003epop_get2() {\n\t\tif (deq_.size() \u003c 2)return { zero_,zero_ };\n\t\tdouble v1{ deq_.front() };\n\t\tdeq_.pop_front();\n\t\tdouble v2{ deq_.front() };\n\t\tdeq_.pop_front();\n\t\treturn { v2,v1 };\n\t}\n\n\tdouble optor(const std::string op) {\n\t\tstd::map\u003cstd::string, double(*)(double, double)\u003eopmap{\n\t\t\t{\"+\",[](double l,double r) {return l + r; }},\n\t\t\t{\"-\",[](double l,double r) {return l - r; }},\n\t\t\t{\"*\",[](double l,double r) {return l * r; }},\n\t\t\t{\"/\",[](double l,double r) {return l / r; }},\n\t\t\t{\"^\",[](double l,double r) {return std::pow(l,r); }},\n\t\t\t{\"%\",[](double l,double r) {return std::fmod(l,r); }}\n\t\t};\n\t\tif (opmap.find(op) == opmap.end())return zero_;\n\t\tauto [l, r] = pop_get2();\n\t\tif (op == \"/\" \u0026\u0026 r == zero_)deq_.push_front(inf_);\n\t\telse deq_.push_front(opmap.at(op)(l, r));\n\t\treturn deq_.front();\n\t}\n\npublic:\n\tdouble op(const std::string\u0026 s) {\n\t\tif (is_numeric(s)) {\n\t\t\tdouble v{ std::stod(s) };\n\t\t\tdeq_.push_front(v);\n\t\t\treturn v;\n\t\t}\n\t\telse return optor(s);\n\t}\n\n\tvoid clear() {\n\t\tdeq_.clear();\n\t}\n\n\tstd::string get_stack_string()const {\n\t\tstd::string s{};\n\t\tfor (const auto\u0026 v : deq_) {\n\t\t\ts += std::format(\"{} \", v);\n\t\t}\n\t\treturn s;\n\t}\n};\n\nint main() {\n\tRPN rpn;\n\tfor (std::string o{}; std::cin \u003e\u003e o;) {\n\t\trpn.op(o);\n\t\tauto stack_str{ rpn.get_stack_string() };\n\t\tprint(\"{}: {}\\n\", o, stack_str);\n\t}\n}\n```\n\n### [3.12使用`std::map`的词频计数器](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.12%E4%BD%BF%E7%94%A8map%E7%9A%84%E8%AF%8D%E9%A2%91%E8%AE%A1%E6%95%B0%E5%99%A8.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cranges\u003e\n#include\u003cregex\u003e\n#include\u003cvector\u003e\n\nnamespace stdr = std::ranges;\nnamespace regex_constants = std::regex_constants;\nnamespace bw { constexpr const char* re{ \"(\\\\w+)\" }; }\n\nint main() {\n\tstd::map\u003cstd::string, int\u003ewordmap{};\n\tstd::vector\u003cstd::pair\u003cstd::string, int\u003e\u003ewordvec{};\n\tstd::regex word_re(bw::re);\n\tsize_t total_words{};\n\n\tfor (std::string s{}; std::cin \u003e\u003e s;) {\n\t\tauto words_begin{ std::sregex_iterator(s.begin(),s.end(),word_re) };\n\t\tauto words_end{ std::sregex_iterator() };\n\t\tfor (auto r_it{ words_begin }; r_it != words_end; ++r_it) {\n\t\t\tstd::smatch match{ *r_it };//字符串匹配类\n\t\t\tauto word_str{ match.str() };//得到输入的单词\n\t\t\tstdr::transform(word_str, word_str.begin(), [](uint8_t c) {return tolower(c); });//将字母全部大写\n\t\t\tauto [map_it, result] = wordmap.try_emplace(word_str, 0);//插入到map中，map的键不会有重复，自动去重\n\t\t\tauto\u0026 [w, count] = *map_it;\n\t\t\t++total_words;\n\t\t\t++count;//增加单词计数\n\t\t}\n\t}\n\tauto unique_words = wordmap.size();\n\twordvec.reserve(unique_words);\n\tstdr::move(wordmap, std::back_inserter(wordvec));\n\tstdr::sort(wordvec, [](const auto\u0026 a, const auto\u0026 b) {\n\t\treturn (a.second != b.second) ? (a.second \u003e b.second) : (a.first \u003c b.first);\n\t});\n\n\tprint(\"unique word count: {}\\n\", total_words);//总共的单词个数\n\tprint(\"unqiue word count: {}\\n\", unique_words);//去除重复之后的\n\tfor (int limit{ 20 }; auto \u0026 [w, count]:wordvec) {\n\t\tprint(\"{}: {}\\n\", count, w);\n\t\t//if (--limit == 0)break;\n\t}\n}\n```\n\n### [3.13找出含有相应长句的`std::vector`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.13%E6%89%BE%E5%87%BA%E5%90%AB%E6%9C%89%E7%9B%B8%E5%BA%94%E9%95%BF%E5%8F%A5%E7%9A%84vector.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cstring_view\u003e\n#include\u003cvector\u003e\n#include\u003cranges\u003e\nnamespace stdr = std::ranges;\n\nbool is_eos(const std::string_view\u0026 str) {\n\tconstexpr const char* end_punct{ \".!?\" };\n\tfor (auto c : str) {\n\t\tif (strchr(end_punct, c) != nullptr)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nint main() {\n\tstd::vector\u003cstd::vector\u003cstd::string\u003e\u003evv_sentences{ std::vector\u003cstd::string\u003e{} };\n\tfor (std::string s{}; std::cin \u003e\u003e s;) {\n\t\tvv_sentences.back().emplace_back(s);\n\t\tif (is_eos(s)) {\n\t\t\tvv_sentences.emplace_back(std::vector\u003cstd::string\u003e{});\n\t\t}\n\t}\n\n\tif (vv_sentences.back().empty())vv_sentences.pop_back();\n\tstdr::sort(vv_sentences, [](const auto\u0026 l, const auto\u0026 r) {\n\t\treturn l.size() \u003e r.size();\n\t});\n\n\tfor (const auto\u0026 v : vv_sentences) {\n\t\tsize_t size = v.size();\n\t\tprint(\"{}: \", size);\n\t\tfor (const auto\u0026 s : v) {\n\t\t\tprint(\"{} \", s);\n\t\t}\n\t\tprint(\"\\n\");\n\t}\n\tprint(\"\\n\");\n}\n```\n[std::ranges::sort](https://zh.cppreference.com/w/cpp/algorithm/ranges/sort)和`std::sort`类似，只是我们可以省略排序的范围了\n\n[strchr](https://zh.cppreference.com/w/c/string/byte/strchr)函数是C库下的函数，在标头 `\u003cstring.h\u003e` 中定义为：`char *strchr( const char *str, int ch )`，用于从str中查找ch字符首次出现的位置，若未找到则返回`NULL`\n### [3.14使用`std::multimap`制作待办事项](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/3.14%E4%BD%BF%E7%94%A8multimap%E5%88%B6%E4%BD%9C%E5%BE%85%E5%8A%9E%E4%BA%8B%E9%A1%B9.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cmap\u003e\n\nint main() {\n\tstd::multimap\u003cint, std::string\u003etodo{\n\t\t{1,\"🤣\"},\n\t\t{1,\"🥵\"},\n\t\t{3,\"🐴\"},\n\t\t{4,\"c\"}\n\t};\n\trprint(todo);\n}\n```\n运行结果：\n```\n😘\n🐴\n🥵\n🤣\n```\n\n`std::multimap`允许存在多个同名的键，默认对键值进行升序排列，你也可以自己指定排序的方式，同时，它还支持迭代，所以，在大多数场景下，`std::multimap`有着`priority_queue`更灵活的特性。而且，因为其支持迭代且有序，所以你也可以使用`lower_bound` 和`upper_bound`来对键值进行查找\n\n### 第三章总结\n第三章内容较多，需要对STL容器有一定的了解，建议每一个demo都自己写完理解意义后再往下阅读。\n\n---\n\n\u003cbr\u003e\n\n## 第四章 兼容迭代器\n### [4.3创建可迭代范围](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/4.3%E5%88%9B%E5%BB%BA%E5%8F%AF%E8%BF%AD%E4%BB%A3%E8%8C%83%E5%9B%B4.cpp)\n```cpp\n#include\"print.h\"\n\ntemplate\u003cclass T\u003e\nclass Seq {\n\tT _star{};\n\tT _end{};\npublic:\n\tSeq(T star, T end) :_star(star), _end(end) {}\n\tstruct iterator {\n\t\tT value{};\n\t\texplicit iterator(T v) :value(v) {}\n\t\titerator\u0026 operator++() {\n\t\t\tvalue++;\n\t\t\treturn *this;\n\t\t}\n\t\tT operator*() {\n\t\t\treturn value;\n\t\t}\n\t\tbool operator!=(const iterator\u0026 l) {\n\t\t\treturn this-\u003evalue != l.value;\n\t\t}\n\t};\n\titerator begin() {\n\t\treturn iterator{ _star };\n\t}\n\titerator end() {\n\t\treturn iterator{ _end };\n\t}\n};\n\ntemplate\u003cclass T,size_t size\u003e\nstruct X {\n\tT array[size]{};\n\tT* begin() {\n\t\treturn array;\n\t}\n\tT* end() {\n\t\treturn array + size;\n\t}\n};\n\nint main() {\n\tSeq\u003cint\u003ev{ 1,10 };\n\tfor (auto i : v) {\n\t\tprint(\"{} \", i);\n\t}\n\tprint(\"\\n\");\n\n\tX\u003cint, 10\u003ex{ 1,2,3,4,5,6,7,8,9,10 };\n\tfor (auto i : x) {\n\t\tprint(\"{} \", i);\n\t}\n}\n```\n欲让类支持[range-for ](https://zh.cppreference.com/w/cpp/language/range-for)循环，需是定义了 `begin` 和 `end` 成员函数或自由函数(也就是非成员函数)的对象，在`Seq`类中，我们已经定义了`begin`和`end`函数，他们都返回一个`iterator`类。当然，这样是不够的，如普通的循环一般，`iterator`对象需支持`++`和`!=` ，才能正确的被循环使用（迭代器循环：`for(T::iterator it{obj.begin()}; it != obj.end(); it ++)`）\n\n而`X`类里面所迭代的对象是指针，指针本身是支持判等和自增操作的，所以不需要再去实现。\n\n### [4.4使迭代器与STL迭代器特性兼容](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/4.4%E4%BD%BF%E8%BF%AD%E4%BB%A3%E5%99%A8%E4%B8%8ESTL%E8%BF%AD%E4%BB%A3%E5%99%A8%E7%89%B9%E6%80%A7%E5%85%BC%E5%AE%B9.cpp)\n```cpp\n#include\"print.h\"\n\ntemplate\u003cclass T\u003e\nclass Seq {\n\tT _star{};\n\tT _end{};\npublic:\n\tSeq(T star, T end) :_star(star), _end(end) {}\n\tstruct iterator {\n\t\tT value{};\n\n\t\tusing value_type        = std::remove_cv_t\u003cT\u003e;\n\t\tusing difference_type   = std::ptrdiff_t;\n\t\tusing pointer           = const T*;\n\t\tusing reference         = const T\u0026;\n\n\t\texplicit iterator(T v=0) :value(v) {}\n\t\titerator\u0026 operator++() {\n\t\t\tvalue++;\n\t\t\treturn *this;\n\t\t}\n\t\titerator operator++(int) {\n\t\t\tauto t{ *this };\n\t\t\t++ *this;\n\t\t\treturn t;\n\t\t}\n\t\tT operator*()const {\n\t\t\treturn value;\n\t\t}\n\t\tbool operator!=(const iterator\u0026 l)const noexcept{\n\t\t\treturn this-\u003evalue != l.value;\n\t\t}\n\t\tbool operator==(const iterator\u0026 l)const noexcept {\n\t\t\treturn this-\u003evalue == l.value;\n\t\t}\n\t};\n\titerator begin()const {\n\t\treturn iterator{ _star };\n\t}\n\titerator end()const {\n\t\treturn iterator{ _end };\n\t}\n};\n\ntemplate\u003cclass T\u003e\nrequires std::forward_iterator\u003ctypename T::iterator\u003e\nvoid printc(const T\u0026 r) {\n\tfor (const auto\u0026 i : r) {\n\t\tprint(\"{} \", i);\n\t}\n\tprint(\"\\n\");\n}\n\nint main() {\n\tSeq\u003cint\u003er{ 100,110 };\n\t//auto [min_it, max_it] = std::minmax_element(r.begin(), r.end());\n\tauto [min_it, max_it] = std::ranges::minmax_element(r);\n\tprint(\"max:{} min:{}\\n\", max_it.value, min_it.value);\n\tprintc(r);\n\tstatic_assert(std::ranges::forward_range\u003cSeq\u003cint\u003e\u003e);\n}\n```\n\n### [4.5使用迭代器适配器填充STL容器](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/4.5%E4%BD%BF%E7%94%A8%E8%BF%AD%E4%BB%A3%E5%99%A8%E9%80%82%E9%85%8D%E5%99%A8%E5%A1%AB%E5%85%85STL%E5%AE%B9%E5%99%A8.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cstring\u003e\n#include\u003cdeque\u003e\n#include\u003calgorithm\u003e\n#include\u003cvector\u003e\n#include\u003csstream\u003e\n\ninline void printc(const std::ranges::range auto\u0026 v,std::string_view s=\"\") {\n\tif (s.size())print(\"{}: \", s);\n\tprint(\"size: {}  \", v.size());\n\tprint(\"[ \");\n\tfor (const auto\u0026 i : v)print(\"{} \", i);\n\tprint(\"]\\n\");\n}\n\nint main() {\n\tstd::deque\u003cint\u003ed1{ 1,2,3,4,5 };\n\tstd::deque\u003cint\u003ed2(d1.size());\n\tstd::copy(d1.begin(), d1.end(), d2.begin());\n\tprintc(d2, \"d2 after copy\");\n\n\tstd::copy(d1.begin(), d1.end(), std::back_inserter(d2));\n\tprintc(d2, \"d2 after back_inserter\");\n\n\tstd::deque\u003cint\u003ed3{ 47,73,114,138,54 };\n\tstd::copy(d3.begin(), d3.end(), std::front_inserter(d2));\n\tprintc(d2, \"d2 after front_inserter\");\n\n\tauto it2{ d2.begin() + 2 };\n\tstd::copy(d1.begin(), d1.end(), std::inserter(d2, it2));\n\tprintc(d2, \"d2 after minddle insert\");\n\n\tprint(\"ostream_iterator:\");\n\tstd::copy(d1.begin(), d1.end(), std::ostream_iterator\u003cint\u003e{std::cout,\" \"});\n\tprint(\"\\n\");\n\t\n\t/*std::vector\u003cstd::string\u003evs{};\n\tstd::copy(std::istream_iterator\u003cstd::string\u003e(std::cin), {}, std::back_inserter(vs));\n\tprintc(vs, \"vs2\");*/\n\n\t/*std::vector\u003cint\u003e V(std::istream_iterator\u003cint\u003e(std::cin), {});\n\tprintc(V, \"V\");*/\n\n\tfor (auto it = std::istream_iterator\u003cstd::string\u003e(std::cin);\n\t\tit != std::istream_iterator\u003cstd::string\u003e{}; ++it) {\n\t\tprint(\"{} \", *it);\n\t}\n}\n```\n\n### [4.6创建一个迭代器生成器](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/4.6%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%BF%AD%E4%BB%A3%E5%99%A8%E7%94%9F%E6%88%90%E5%99%A8.cpp)\n```cpp\n#include\"print.h\"\n\nclass fib_generator {\n\tusing fib_t = unsigned long;\n\tfib_t stop_{};\n\tfib_t count_{ 0 };\n\tfib_t a_{ 0 };\n\tfib_t b_{ 1 };\n\tconstexpr void do_fib() {\n\t\tconst fib_t old_d = b_;\n\t\tb_ += a_;\n\t\ta_ = old_d;\n\t}\npublic:\n\tusing iterator_concept = std::forward_iterator_tag;\n\tusing iterator_category = std::forward_iterator_tag;\n\tusing value_type = std::remove_cv_t\u003cfib_t\u003e;\n\tusing difference_type = std::ptrdiff_t;\n\tusing pointer = const fib_t*;\n\tusing reference = const fib_t\u0026;\n\n\texplicit fib_generator(fib_t stop = 0) :stop_{ stop } {}\n\n\tfib_t operator*()const { return b_; }\n\tconstexpr fib_generator\u0026 operator++() {\n\t\tdo_fib();\n\t\t++count_;\n\t\treturn *this;\n\t}\n\tfib_generator operator++(int) {\n\t\tauto tmp{ *this };\n\t\t++* this;\n\t\treturn tmp;\n\t}\n\tbool operator==(const fib_generator\u0026 o)const {\n\t\treturn count_ == o.count_;\n\t}\n\tconst fib_generator\u0026 begin()const { return *this; }\n\tconst fib_generator end()const {\n\t\tauto sentinel = fib_generator();\n\t\tsentinel.count_ = stop_;\n\t\treturn sentinel;\n\t}\n\tfib_t size() { return stop_; }\n};\n\nint main() {\n\tprintc(fib_generator(10));\n\tfib_generator fib(10);\n\tauto x = std::ranges::views::transform(fib, [](auto x) {return x * x; });\n\tprintc(x, \"squared:\");\n}\n```\n\n### [4.7反向迭代器的反向适配器](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/4.7%E5%8F%8D%E5%90%91%E8%BF%AD%E4%BB%A3%E5%99%A8%E7%9A%84%E5%8F%8D%E5%90%91%E9%80%82%E9%85%8D%E5%99%A8.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cstring\u003e\n#include\u003cvector\u003e\n\nvoid printr(const auto\u0026 r, std::string_view s = \"\") {\n\tauto rbegin = std::rbegin(r);\n\tauto rend = std::rend(r);\n\tfor (auto it = rbegin; it != rend; ++it) {\n\t\tprint(\"{} \", *it);\n\t}\n\tprint(\"\\n\");\n}\n\nint main() {\n\tint array[]{ 1,2,3,4,5 };\n\tprintc(array, \"c-array\");\n\tauto it = std::begin(array);\n\tauto it_end = std::end(array);\n\twhile (it != it_end) {\n\t\tprint(\"{} \", *it++);\n\t}\n\tprint(\"\\n\");\n\n\tauto it2 = std::rbegin(array);\n\tauto it_end2 = std::rend(array);\n\twhile (it2 != it_end2) {\n\t\tprint(\"{} \", *it2++);\n\t}\n\tprint(\"\\n\");\n\n\tprintr(array, \"rev c-array\");\n\n\tstd::vector\u003cint\u003ev{ 1,2,3,4,5 };\n\tprintc(v, \"vector\");\n\tprintr(v, \"rev vector\");\n}\n```\n\n### [4.8用哨兵迭代未知长度的对象](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/4.8%E7%94%A8%E5%93%A8%E5%85%B5%E8%BF%AD%E4%BB%A3%E6%9C%AA%E7%9F%A5%E9%95%BF%E5%BA%A6%E7%9A%84%E5%AF%B9%E8%B1%A1.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cstring\u003e\n\nclass cstr_it {\n\tconst char* s{};\n\tstatic constexpr const char nullchar = '\\0';\npublic:\n\texplicit cstr_it(const char* str) :s{ str } {}\n\tchar operator*()const { return *s; }\n\tcstr_it\u0026 operator++() {\n\t\t++s;\n\t\treturn *this;\n\t}\n\tbool operator!=(const char)const {\n\t\treturn s != nullptr \u0026\u0026 *s != nullchar;\n\t}\n\tcstr_it begin()const { return *this; }\n\tconst char end()const { return nullchar; }\n};\n\nvoid print_cstr(const char* s) {\n\tprint(\"{}: \", s);\n\tfor (char c : cstr_it(s)) {\n\t\tprint(\"{:02x} \", c);\n\t}\n\tprint(\"\\n\");\n}\n\nint main() {\n\tconst char carray[]{ \"array\" };\n\tprint_cstr(carray);\n\tconst char* cstr{ \"c-string\" };\n\tprint_cstr(cstr);\n}\n```\n\n### [4.9构建zip迭代器适配器](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/4.9%E6%9E%84%E5%BB%BAzip%E8%BF%AD%E4%BB%A3%E5%99%A8%E9%80%82%E9%85%8D%E5%99%A8.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003cstring\u003e\n\ntemplate\u003ctypename T\u003e\nclass zip_iterator {\n\tusing val_t = typename T::value_type;\n\tusing ret_t = std::pair\u003cval_t, val_t\u003e;\n\tusing it_t = typename T::iterator;\n\n\tit_t ita_{};\n\tit_t itb_{};\n\tit_t ita_begin_{};\n\tit_t itb_begin_{};\n\tit_t ita_end_{};\n\tit_t itb_end_{};\n\tzip_iterator(it_t ita, it_t itb) :ita_{ ita }, itb_{ itb } {}//用作begin和end返回的迭代器构造函数\n\npublic:\n\tusing value_type = std::pair\u003cval_t, val_t\u003e;\n\tusing difference_type = long int;\n\tusing pointer = const val_t*;\n\tusing reference = const val_t\u0026;\n\n\tzip_iterator(T\u0026 a, T\u0026 b) :\n\t\tita_{ a.begin() }, itb_{ b.begin() }, ita_begin_{ ita_ }, itb_begin_{ itb_ }, ita_end_{ a.end() }, itb_end_{ b.end() } {}\n\tzip_iterator\u0026 operator++() {\n\t\t++ita_;\n\t\t++itb_;\n\t\treturn *this;\n\t}\n\tbool operator==(const zip_iterator\u0026 o)const { return ita_ == o.ita_ || itb_ == o.itb_; }\n\tbool operator!=(const zip_iterator\u0026 o)const { return !operator==(o); }\n\tret_t operator*()const {return { *ita_,*itb_ };}\n\tzip_iterator begin()const { return { ita_begin_,itb_begin_ }; }\n\tzip_iterator end()const { return { ita_end_,itb_end_ }; }\n};\n\nint main() {\n\tstd::vector\u003cstd::string\u003evec_a{ \"Bob\",\"John\",\"Joni\",\"?\" };\n\tstd::vector\u003cstd::string\u003evec_b{ \"Dylan\",\"Williams\",\"Mitchell\" };\n\n\tprintc(vec_a, \"vec_a: \");\n\tprintc(vec_b, \"vec_b: \");\n\n\tprint(\"zipped: \");\n\tfor (const auto\u0026 [a, b] : zip_iterator{ vec_a, vec_b }) {\n\t\tprint(\"[{}, {}] \", a, b);\n\t}\n\tprint(\"\\n\");\n\n\tstd::map\u003cstd::string, std::string\u003ename_map{};\n\tfor (auto [a, b] : zip_iterator{ vec_a,vec_b }) {//插入到map中\n\t\tname_map.emplace(a, b);\n\t}\n\tprint(name_map);//打印\n\n}\n```\n\n### [4.10创建随机访问迭代器](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/4.10%E5%88%9B%E5%BB%BA%E9%9A%8F%E6%9C%BA%E8%AE%BF%E9%97%AE%E8%BF%AD%E4%BB%A3%E5%99%A8.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cmemory\u003e\n#include\u003cvector\u003e\n\ntemplate\u003ctypename T\u003e\nclass Container {\n\n\tclass iterator {\n\n\t\tT* ptr_;\n\tpublic:\n\t\tusing iterator_concept = std::contiguous_iterator_tag;\n\t\tusing iterator_category = std::contiguous_iterator_tag;\n\t\tusing value_type = std::remove_cv_t\u003cT\u003e;\n\t\tusing difference_type = std::ptrdiff_t;\n\t\tusing pointer = const T*;\n\t\tusing reference = const T\u0026;\n\n\t\titerator(T* ptr_ = nullptr) :ptr_{ ptr_ } {}\n\n\t\tconst auto operator\u003c=\u003e(const iterator\u0026 o)const {\n\t\t\tptr_ \u003c=\u003e o.ptr_;\n\t\t}\n\n\t\titerator operator+(size_t size)const {\n\t\t\treturn { ptr_ + size };\n\t\t}\n\n\t\tfriend const iterator operator+(size_t size, const iterator\u0026 o) {\n\t\t\treturn { o.ptr_ + size };\n\t\t}\n\n\t\tconst iterator operator-(size_t size)const {\n\t\t\treturn{ ptr_ - size };\n\t\t}\n\n\t\tconst size_t operator-(const iterator\u0026 o)const {\n\t\t\treturn  ptr_ - o.ptr_ ;\n\t\t}\n\n\t\titerator\u0026 operator++() {\n\t\t\t++ptr_;\n\t\t\treturn *this;\n\t\t}\n\n\t\titerator operator++(int) {\n\t\t\tauto tmp{ *this };\n\t\t\t++* this;\n\t\t\treturn tmp;\n\t\t}\n\n\t\titerator\u0026 operator--() {\n\t\t\t--ptr_;\n\t\t\treturn *this;\n\t\t}\n\n\t\titerator operator--(int) {\n\t\t\tauto tmp{ *this };\n\t\t\t--* this;\n\t\t\treturn tmp;\n\t\t}\n\n\t\titerator\u0026 operator+=(size_t size)const {\n\t\t\tptr_ += size;\n\t\t\treturn *this;\n\t\t}\n\n\t\titerator\u0026 operator-=(size_t size)const {\n\t\t\tptr_ -= size;\n\t\t\treturn *this;\n\t\t}\n\n\t\tconst reference operator[](size_t size)const {\n\t\t\treturn ptr_[size];\n\t\t}\n\n\t\tconst bool operator==(const iterator\u0026 o)const {\n\t\t\treturn ptr_ == o.ptr_;\n\t\t}\n\n\t\tbool operator!=(const iterator\u0026 o)const {\n\t\t\treturn ptr_ != o.ptr_;\n\t\t}\n\n\t\treference operator*()const {\n\t\t\treturn *ptr_;\n\t\t}\n\n\t\tT* operator-\u003e()const {\n\t\t\treturn ptr_;\n\t\t}\n\n\t};\n\n\tsize_t n_elements_{};\n\tstd::unique_ptr\u003cT[]\u003ec_{};\npublic:\n\tContainer(std::initializer_list\u003cT\u003el) :n_elements_{ l.size() }, c_{ std::make_unique\u003cT[]\u003e(n_elements_) }\n\t{\n\t\tfor (size_t index{}; auto e : l) {\n\t\t\tc_[index++] = e;\n\t\t}\n\t}\n\n\tContainer(size_t sz) :n_elements_{ sz }, c_{ std::make_unique\u003cT[]\u003e(n_elements_) } {}\n\n\tsize_t size()const {\n\t\treturn n_elements_;\n\t}\n\n\tconst T\u0026 operator[](size_t index)const {\n\t\treturn c_[index];\n\t}\n\n\tconst T\u0026 at(size_t index)const {\n\t\treturn index \u003c n_elements_ ? c_[index] : throw std::out_of_range{ \"Container::at(): index out of range\" };\n\t}\n\n\tbool empty() const {\n\t\treturn (n_elements_ == 0);\n\t}\n\n\titerator begin()const { return { c_.get() }; }\n\titerator end()const { return { c_.get() + n_elements_ }; }\n};\n\ntemplate\u003ctypename T\u003e\nContainer(std::initializer_list\u003cT\u003el) -\u003e Container\u003cT\u003e;\n\nstruct X {\n\tint x;\n};\n\nint main() {\n\tContainer v{1,2,3,4,5};\n\tfor (const auto\u0026 i : v) {\n\t\tprint(\"{} \", i);\n\t}\n\tprint(\"\\n\");\n\n\tContainer\u003cX\u003ev3{ {1},{2} };\n\tauto ret2 = v3.begin();\n\tret2-\u003ex;\n\tprint(\"{}\\n\", std::ranges::forward_range\u003cContainer\u003cint\u003e\u003e);\n\tprint(\"{}\\n\", std::ranges::range\u003cContainer\u003cint\u003e\u003e);\n\tprint(\"{}\\n\", std::ranges::viewable_range\u003cContainer\u003cint\u003e\u003e);\n\tprint(\"{}\\n\", std::bidirectional_iterator\u003cContainer\u003cint\u003e\u003e);\n\n\tfor (const auto\u0026 i : v | std::views::reverse) {\n\t\tprint(\"{} \", i);\n\t}\n\t\n}\n```\n### 第四章总结\n关于这个迭代器的内容，书上这些demo总的来说还是可以的，值得慢慢看，最好都是自己照着写一遍就行。\n\n---\n\n\u003cbr\u003e\n\n## 第五章 lambda表达式\n### [5.3用于作用域可重用代码](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/5.3%E7%94%A8%E4%BA%8E%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%8F%AF%E9%87%8D%E7%94%A8%E4%BB%A3%E7%A0%81.cpp)\n```cpp\n#include\"print.h\"\n\nint main() {\n\tauto one = []() {return \"one\"; };\n\tauto two = [] {return \"two\"; };\n\tprint(\"{} {}\\n\", one(), two());\n\n\tauto p = [](auto f) {//泛型lambda，C++20之前只能使用这种方式\n\t\tprint(\"{}\\n\", f()); \n\t};\n\tp([] {return \"乐\"; });\n\n\tauto p2 = []\u003ctypename T\u003e(T\u0026\u0026 f) { print(\"{}\\n\", f()); };\n\tp2(one);\n\tp2(std::move(one));\n\n\t[] \u003ctypename T\u003e(T\u0026\u0026 f) { print(\"{}\\n\", f()); }(two);\n\n\tint num{};\n\tauto p3 = [num]()mutable {num++; };\n\tfor (auto i = 0; i \u003c 5; i++)p3();\n\n\tprint(\"{}\\n\", num);\n\n\tauto p4 = [\u0026]()mutable {num++; };\n\tprint(\"{}\\n\", sizeof(p4));\n\n\tconstexpr int n = []()constexpr {return 10 * 10; }();\n\n\tauto p5 = []()-\u003eint {return 10; };\n}\n```\n\n### [5.4算法库中作为谓词](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/5.4%E7%AE%97%E6%B3%95%E5%BA%93%E4%B8%AD%E4%BD%9C%E4%B8%BA%E8%B0%93%E8%AF%8D.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n\nbool is_div4(int i) {\n\treturn i % 4 == 0;\n}\n\nstruct is_div4_ {\n\tbool operator()(int i) {\n\t\treturn i % 4 == 0;\n\t}\n};\n\nauto is_div_by(int divisor) {\n\treturn [=](int i) {return i % divisor == 0; };\n}\n\nint main() {\n\tstd::vector v{ 1,2,3,4,44,8,10 };\n\tauto count1 =std::count_if(v.begin(), v.end(), is_div4);\n\tauto count2 = std::count_if(v.begin(), v.end(), is_div4_{});\n\tprint(\"{} {}\\n\", count1, count2);\n\tauto count3 = std::count_if(v.begin(), v.end(), [](int i) {return i % 4 == 0; });\n\tprint(\"{}\\n\", count3);\n\n\tfor (int i : {3, 4, 5}) {\n\t\tauto count = std::ranges::count_if(v, is_div_by(i));\n\t\tprint(\"{} \", count);\n\t}\n\n\t//不带捕获的lambda表达式可以有转换函数，隐式转换到对应的函数指针\n\tint(*p)(int) = [](int a) {return a; };\n\tprint(\"{}\\n\", p(10));\n}\n```\n\n### [5.5与`std::function`一起作为多态包装器](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/5.5%E4%B8%8Efunction%E4%B8%80%E8%B5%B7%E4%BD%9C%E4%B8%BA%E5%A4%9A%E6%80%81%E5%8C%85%E8%A3%85%E5%99%A8.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003cfunctional\u003e\n#include\u003clist\u003e\n#include\u003cdeque\u003e\n\nvoid hello() {\n\tprint(\"hello\\n\");\n}\n\nstruct Hello_ {\n\tvoid greeting() {\n\t\tprint(\"hello\\n\");\n\t}\n};\n\nint main() {\n\tstd::deque\u003cint\u003ed;\n\tstd::list\u003cint\u003el;\n\tstd::vector\u003cint\u003ev;\n\n\tauto print_c = [](const auto\u0026 c) {\n\t\tfor (const auto\u0026 i : c)print(\"{} \", i);\n\t\tprint(\"\\n\");\n\t};\n\tauto push_c = [](auto\u0026 container) {\n\t\treturn [\u0026container](auto value) {\n\t\t\tcontainer.push_back(value);\n\t\t};\n\t};\n\tconst std::vector\u003cstd::function\u003cvoid(int)\u003e\u003econsumers{ push_c(d),push_c(l),push_c(v) };\n\t//consumers[0](10);\n\t//print_c(d);\n\tfor (auto\u0026 i : consumers) {\n\t\tfor (size_t j = 0; j \u003c 10; j++) {\n\t\t\ti(j);\n\t\t}\n\t}\n\tprint_c(d);\n\tprint_c(l);\n\tprint_c(v);\n\n\tstd::function f{ hello };\n\tf();\n\tHello_ h;\n\tstd::function\u003cvoid(void)\u003eff{ std::bind(\u0026Hello_::greeting,\u0026h) };\n\tff();\n\tstd::bind(\u0026Hello_::greeting, \u0026h)();\n}\n```\n\n### [5.6用递归连接lambda](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/5.6%E7%94%A8%E9%80%92%E5%BD%92%E8%BF%9E%E6%8E%A5lambda.cpp)\n```cpp\n#include\"print.h\"\ntemplate \u003ctypename F, typename ...Ts\u003e\nauto concat(F t, Ts ...ts){\n\tif constexpr (sizeof...(ts) \u003e 0) {\n\t\treturn [=](auto ...parameters) {\n\t\t\treturn t(concat(ts...)(parameters...));\n\t\t};\n\t}\n\telse {\n\t\treturn t;\n\t}\n}\nint main() {\n\tauto twice = [](auto i) {return i * 2; };\n\tauto thrice = [](auto i) {return i * 3; };\n\tauto combined = concat(thrice, twice, std::plus\u003cint\u003e{});\n\tprint(\"{} \\n\", combined(2, 3));\n}\n```\n`concat`较为复杂，所以先看传入一个参数时的情况。\n```cpp\nauto combined = concat(std::plus\u003cint\u003e{});\nprint(\"{} \\n\", combined(2, 3));\n```\n因为只有一个参数`t`，`sizeof...(ts) \u003e 0`求值结果为`flase`，所以函数直接返回传入的参数，即`plus\u003cint\u003e`对象。\n`concat`返回值初始化`combined`，`combined(2, 3)`的结果是5。\u003cbr\u003e\n再来看传入两个参数时的情况。\n```cpp\nauto combined = concat(twice, std::plus\u003cint\u003e{});\n```\n为了便于理解，不妨手动实例化`concat`模板：\n```cpp\n//针对 concat(twice, std::plus\u003cint\u003e{}) 实例化后的伪代码，假设 twice_type 是 twice 的类型\nint concat(twice_type t, std::plus\u003cint\u003e ts){\n\tif constexpr (1 \u003e 0) {\n\t\treturn [=](auto ...parameters) {\n\t\t\treturn t(concat(ts)(parameters...));\n\t\t};\n\t}\n\telse {\n\t\treturn t;\n\t}\n}\n```\n这时`sizeof...(ts) \u003e 0`求值为`true`，`concat`返回一个 lambda 表达式。这个 lambda 又返回了`twice`\n和`plus\u003cint\u003e`复合调用。这是因为`concat(std::plus\u003cint\u003e{})`正是一个参数时的情况，返回值是`std::plus\u003cint\u003e{}`。\n`combined`被这个由`twice`和`plus\u003cint\u003e`复合调用的lambda表达式初始化，`combined(2, 3)`结果是10。\u003cbr\u003e\n结合以上两个例子，可以知道`concat`的作用就是将多个函数(可调用对象)进行连接调用，后一个函数的返回值作为前一个函数的参数，\n最后一个函数的参数是由新生成函数的参数传入。这类似于数学函数的复合\u003cbr\u003e\n$$ (f \\circ g \\circ h)(x) = f(g(h(x))) $$\u003cbr\u003e\n最后来看书中三个参数的情况。`concat`递归的复合`thrice`、`twice`、`std::plus\u003cint\u003e{}`三个可调用对象，\n并用lambda包装后返回并初始化`combined`，所以`combined(2, 3)`的结果是30。\u003cbr\u003e\n\n\u003cbr\u003e\n\n### [5.7将谓词与逻辑连接词连接起来](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/5.7%E5%B0%86%E8%B0%93%E8%AF%8D%E4%B8%8E%E9%80%BB%E8%BE%91%E8%BF%9E%E6%8E%A5%E8%AF%8D%E8%BF%9E%E6%8E%A5%E8%B5%B7%E6%9D%A5.cpp)\n```cpp\n#include\"print.h\"\n#include \u003cfunctional\u003e\n\nstatic bool begins_with_a(const std::string\u0026 s)\n{\n\treturn s.find(\"a\") == 0;\n}\nstatic bool ends_with_b(const std::string\u0026 s)\n{\n\treturn s.rfind(\"b\") == s.length() - 1;\n}\n\ntemplate \u003ctypename A, typename B, typename F\u003e\nauto combine(F binary_func, A a, B b) {\n\treturn [=](auto param) {\n\t\treturn binary_func(a(param), b(param));\n\t};\n}\n\nint main() {\n\tauto a_xxx_b{ combine(std::logical_and\u003cint\u003e{},begins_with_a, ends_with_b) };\n\n\tstd::copy_if(std::istream_iterator\u003cstd::string\u003e{std::cin}, {},\n\t\tstd::ostream_iterator\u003cstd::string\u003e{std::cout, \", \"}, a_xxx_b);\n\tstd::cout \u003c\u003c '\\n';\n}\n```\n\n### [5.8用相同的输入调用多个lambda](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/5.8%E7%94%A8%E7%9B%B8%E5%90%8C%E7%9A%84%E8%BE%93%E5%85%A5%E8%B0%83%E7%94%A8%E5%A4%9A%E4%B8%AAlambda.cpp)\n```cpp\n#include\"print.h\"\n\nauto braces(const char a, const char b) {\n\treturn [a, b](const auto v) {\n\t\tprint(\"{}{}{} \", a, v, b);\n\t};\n}\n\nint main() {\n\tauto a = braces('(', ')');\n\tauto b = braces('[', ']');\n\tauto c = braces('{', '}');\n\tauto d = braces('|', '|');\n\tfor (int i : {1, 2, 3, 4, 5}) {\n\t\tfor (auto x : { a,b,c,d }) {\n\t\t\tx(i);\n\t\t}\n\t\tprint(\"\\n\");\n\t}\n}\n```\n\n### [5.9对跳转表使用映射lambda](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/5.9%E5%AF%B9%E8%B7%B3%E8%BD%AC%E8%A1%A8%E4%BD%BF%E7%94%A8%E6%98%A0%E5%B0%84lambda.cpp)\n```cpp\n#include\"print.h\"\n\nconst char prompt(const char* p) {\n    std::string r;\n    print(\"{} \u003e \", p);\n    std::getline(std::cin, r, '\\n');\n\n    if (r.size() \u003c 1) return '\\0';//如果走这个分支，就是直接下一个循环\n    if (r.size() \u003e 1) {\n        print(\"响应时间过长\\n\");\n        return '\\0';\n    }\n    return toupper(r[0]);\n}\n\nint main() {\n    using jumpfunc = void(*)();\n\n    std::map\u003cconst char, jumpfunc\u003e jumpmap{\n        { 'A', [] { print(\"func A\\n\"); } },\n        { 'B', [] { print(\"func B\\n\"); } },\n        { 'C', [] { print(\"func C\\n\"); } },\n        { 'D', [] { print(\"func D\\n\"); } },\n        { 'X', [] { print(\"Bye!\\n\"); } }\n    };\n\n    char select{};\n    while (select != 'X') {\n        if ((select = prompt(\"select A/B/C/D/X\"))) {\n            auto it = jumpmap.find(select);\n            if (it != jumpmap.end()) it-\u003esecond();\n            else print(\"没有对应的选项！\\n\");\n        }\n    }\n}\n```\n\n### 第五章总结\n关于lambda的一些概念，书上描述的是有问题的，不要被误导，视频也提到了。其他的一些demo什么的没问题，都写写。\n\n---\n\n\u003cbr\u003e\n\n## 第六章 STL算法\n### [6.2基于迭代器的复制](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.2%E5%9F%BA%E4%BA%8E%E8%BF%AD%E4%BB%A3%E5%99%A8%E7%9A%84%E5%A4%8D%E5%88%B6.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\nnamespace stdr = std::ranges;\n\nint main() {\n\tstd::vector\u003cstd::string\u003ev1{ \"alpha\",\"beta\",\"gamma\",\"delta\",\"epsilon\" };\n\tprintc(v1,\"v1\");\n\tstd::vector\u003cstd::string\u003ev2(v1.size());\n\tstd::copy(v1.begin(), v1.end(), v2.begin());\n\tprintc(v2, \"v2\");\n\n\tstd::copy(v1.begin(), v1.end(), std::back_inserter(v2));\n\tprintc(v2, \"v2\");\n\n\tstd::vector\u003cstd::string\u003ev3{};\n\tstd::copy_n(v1.begin(), 3, std::back_inserter(v3));\n\tprintc(v3, \"v3\");\n\n\tstd::vector\u003cstd::string\u003ev4{};\n\t/*std::copy_if(v1.begin(), v1.end(), std::back_inserter(v4), [](auto\u0026 s) {\n\t\treturn s.size() \u003e 4;\n\t});*/\n\tstdr::copy_if(v1,std::back_inserter(v4), [](auto\u0026 s) {\n\t\treturn s.size() \u003e 4;\n\t\t});\n\tprintc(v4, \"v4\");\n\n\tstdr::copy(v1, std::ostream_iterator\u003cstd::string\u003e{std::cout, \" \"});\n\tprint(\"\\n\");\n\n\tstdr::move(v1, v2.begin());\n\tprintc(v1, \"after move: v1\");\n\tprintc(v2, \"after move: v2\");\n}\n```\n\n### [6.3将容器元素连接到以供字符串当中](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.3%E5%B0%86%E5%AE%B9%E5%99%A8%E5%85%83%E7%B4%A0%E8%BF%9E%E6%8E%A5%E5%88%B0%E4%BB%A5%E4%BE%9B%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%BD%93%E4%B8%AD.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003csstream\u003e\n#include\u003clist\u003e\n#include\u003cnumbers\u003e\n\nnamespace bw {\n\ttemplate\u003ctypename T\u003e\n\tstd::ostream\u0026 join(T it, T end_it, std::ostream\u0026 o, std::string_view sep = \"\") {\n\t\tif (it != end_it)o \u003c\u003c *it++;\n\t\twhile (it != end_it)o \u003c\u003c sep \u003c\u003c *it++;\n\t\treturn o;\n\t}\n\n\ttemplate\u003ctypename I\u003e\n\tstd::string join(I it, I end_it, std::string_view sep = \"\") {\n\t\tstd::ostringstream ostr;\n\t\tjoin(it, end_it, ostr, sep);\n\t\treturn ostr.str();\n\t}\n\n\tstd::string join(const auto\u0026 c, std::string_view sep = \"\") {\n\t\treturn join(std::begin(c), std::end(c), sep);\n\t}\n}\n\nint main() {\n\tstd::vector\u003cstd::string\u003egreek{ \"alpha\",\"beta\",\"gamma\",\n\t\t\"delta\",\"epsilon\" };\n\tfor (const auto\u0026 c : greek) std::cout \u003c\u003c c \u003c\u003c \",\";\n\tprint(\"\\n\");\n\tauto greek_view = greek | std::views::join;\n\tfor (const auto\u0026 c : greek_view) std::cout \u003c\u003c c;\n\tprint(\"\\n\");\n\n\tbw::join(greek.begin(), greek.end(), std::cout, \", \") \u003c\u003c '\\n';\n\n\tauto s = bw::join(greek.begin(), greek.end(), \", \");\n\tprint(\"{}\\n\", s);\n\n\tauto s2 = bw::join(greek, \", \");\n\tprint(\"{}\\n\", s2);\n\n\tstd::list\u003cdouble\u003elist{ std::numbers::pi,std::numbers::e,std::numbers::sqrt2 };\n\tprint(\"{}\\n\", bw::join(list, \": \"));\n}\n```\n\n### [6.4`std::sort`排序容器元素](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.4sort%E6%8E%92%E5%BA%8F%E5%AE%B9%E5%99%A8%E5%85%83%E7%B4%A0.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003crandom\u003e\n\nvoid check_sorted(auto\u0026 c) {\n\tif (!std::is_sorted(c.begin(), c.end()))print(\"un\");\n\tprint(\"sorted: \");\n}\n\nvoid printc_(const auto\u0026 c) {\n\tcheck_sorted(c);\n\tfor (auto\u0026 e : c)print(\"{} \", e);\n\tprint(\"\\n\");\n}\n\nvoid randomize(auto\u0026 c) {\n\tstatic std::random_device rd;\n\tstatic std::default_random_engine rng(rd());\n\tstd::shuffle(c.begin(), c.end(), rng);\n}\n\nstruct things {\n\tstd::string s_;\n\tint i_;\n\tstd::string str()const {\n\t\treturn std::format(\"({}, {})\", s_, i_);\n\t}\n};\n\nvoid print_things(const auto\u0026 c) {\n\tfor (auto\u0026 v : c)print(\"{} \", v.str());\n\tprint(\"\\n\");\n}\n\nint main() {\n\tstd::vector\u003cint\u003ev{ 1,2,3,4,5,6,7,8,9,10 };\n\tprintc_(v);\n\n\tfor (int i{ 3 }; i; i--) {\n\t\trandomize(v);\n\t\tprintc_(v);\n\t}\n\tstd::sort(v.begin(), v.end());\n\tprintc_(v);\n\n\tprint(\"partial_sort:\\n\");\n\trandomize(v);\n\tauto middle{ v.begin() + (v.size() / 2) };\n\tstd::partial_sort(v.begin(), middle, v.end());\n\tprintc_(v);\n\n\tstd::partition(v.begin(), v.end(), [](int i) {return i \u003e 5; });\n\tprintc_(v);\n\n\tstd::vector\u003cthings\u003evthings{ {\"🐴\",1},{\"😘\",2},{\"🤣\",3},{\"🥵\",4},{\"🤡\",5} };\n\tstd::sort(vthings.begin(), vthings.end(),\n\t\t[](const things\u0026 lhs, const things\u0026 rhs) {\n\t\t\treturn lhs.i_ \u003e rhs.i_;\n\t});\n\n\tprint_things(vthings);\n}\n```\n\n### [6.5`std::transform`修改容器内容](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.5transform%E4%BF%AE%E6%94%B9%E5%AE%B9%E5%99%A8%E5%86%85%E5%AE%B9.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n\nstd::string str_lower(const std::string\u0026 s) {\n\tstd::string outstr{};\n\tfor (const char\u0026 c : s) {\n\t\toutstr += tolower(c);\n\t}\n\treturn outstr;\n}\n\nint main() {\n\tstd::vector\u003cint\u003ev1{ 1,2,3,4,5,6,7,8,9,10 };\n\tstd::vector\u003cint\u003ev2;\n\tprintc(v1, \"v1\");\n\tstd::transform(v1.begin(), v1.end(), std::back_inserter(v2), [](int x) {return x * x; });\n\tprintc(v2, \"v2\");\n\n\tstd::vector\u003cstd::string\u003evstr1{ \"Aaa\",\"Bbb\",\"Ccc\",\"DDD\" };\n\tstd::vector\u003cstd::string\u003evstr2;\n\tprintc(vstr1, \"vstr1\");\n\tprint(\"str_lower:\\n\");\n\tstd::transform(vstr1.begin(), vstr1.end(), std::back_inserter(vstr2),\n\t\t[](std::string\u0026 x) {return str_lower(x); });\n\tprintc(vstr2, \"vstr2\");\n\n\tprint(\"ranges sequares:\\n\");\n\tauto view1 = std::views::transform(v1, [](int x) {return x * x; });\n\tprintc(view1, \"view1\");\n\n\tv2.clear();\n\tstd::ranges::transform(v1, std::back_inserter(v2), [](int x) {return x * x; });\n\tprintc(v2, \"v2\");\n}\n```\n\n\u003cbr\u003e\n\n### [6.6查找特定项](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.6%E6%9F%A5%E6%89%BE%E7%89%B9%E5%AE%9A%E9%A1%B9.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003calgorithm\u003e\n\nstruct City {\n\tstd::string name{};\n\tunsigned pop{};\n\tbool operator==(const City\u0026 o)const {\n\t\treturn name == o.name;\n\t}\n\tstd::string str()const {\n\t\treturn std::format(\"[{}, {}]\", name, pop);\n\t}\n};\n\nint main() {\n\tconst std::vector\u003cint\u003ev{ 1,2,3,4,5,6,7,8,9,10 };\n\n\tauto it1 = std::find(v.begin(), v.end(), 7);\n\tif (it1 != v.end())print(\"found: {}\\n\", *it1);\n\telse print(\"not found:\\n\");\n\n\tconst std::vector\u003cCity\u003ec{\n\t\t{\"London\",8425622},\n\t\t{\"Berlin\",3566791},\n\t\t{\"Tokyo\",37435191},\n\t\t{\"Cairo\",20485965}\n\t};\n\tauto it2 = std::find(c.begin(), c.end(), City{ \"Berlin\" });\n\tif (it2 != c.end())print(\"found: {}\\n\", it2-\u003estr());\n\telse print(\"not found:\\n\");\n\n\tauto it3 = std::find_if(begin(c), end(c), [](const City\u0026 item) {\n\t\treturn item.pop \u003e 20000000;\n\t});\n\tif (it3 != c.end())print(\"found: {}\\n\", it3-\u003estr());\n\telse print(\"not found:\\n\");\n\n\tauto vwl = std::views::filter(c, [](const City\u0026 item) {\n\t\treturn item.pop \u003e 20000000;\n\t});\n\tfor (const City\u0026 e : vwl)print(\"{}\\n\", e.str());\n}\n```\n\n这个内容大概四个部分\n1. 使用[**`std::find`**](https://zh.cppreference.com/w/cpp/algorithm/find)查找标量元素\n2. 使用`std::find`查找自定义类型元素(需要重载`operator==`)\n3. 使用 **`std::find_if`** 查找自定义类型符合谓词要求的元素\n4. 使用 [**`std::views::filter`**](https://zh.cppreference.com/w/cpp/ranges/filter_view) 返回符合谓词要求的视图，可以像普通容器一样遍历\n\n`std::find`或`std::find_if`的返回值是**迭代器**，如果没有查找到，则返回`end()`。\n\n\u003cbr\u003e\n\n### [6.7将容器元素限制在`std::clamp`范围内](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.7%E5%B0%86%E5%AE%B9%E5%99%A8%E5%85%83%E7%B4%A0%E9%99%90%E5%88%B6%E5%9C%A8clamp%E8%8C%83%E5%9B%B4%E5%86%85.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003clist\u003e\n\nconstexpr int ilow{ 0 };\nconstexpr int ihigh{ 500 };\n\nvoid printc_(const std::ranges::range auto\u0026 v, std::string_view s = \"\") {\n\tfor (const auto\u0026 i : v)print(\"{:\u003e5} \", i);\n\tprint(\"\\n\");\n}\n\nint main() {\n\tauto il = { 0,-12,2001,4,5,-14,100,200 };\n\tstd::vector\u003cint\u003evoi{ il };\n\tprint(\"vector voi before:\\n\");\n\tprintc_(voi);\n\n\tprint(\"vector voi after:\\n\");\n\tfor (auto\u0026 e : voi)e = std::clamp(e, ilow, ihigh);\n\tprintc_(voi);\n\n\tprint(\"list loi before:\\n\");\n\tstd::list\u003cint\u003eloi{ il };\n\tprintc_(loi);\n\tstd::transform(loi.begin(), loi.end(), loi.begin(), [](auto e) {\n\t\treturn std::clamp(e, ilow, ihigh);\n\t});\n\tprint(\"list loi after:\\n\");\n\tprintc_(loi);\n}\n```\n运行结果:\n\n\tvector voi before:\n\t0   -12  2001     4     5   -14   100   200\n\tvector voi after:\n\t0     0   500     4     5     0   100   200\n\tlist loi before:\n\t0   -12  2001     4     5   -14   100   200\n\tlist loi after:\n\t0     0   500     4     5     0   100   200\n[**`std::clamp`**](https://zh.cppreference.com/w/cpp/algorithm/clamp) 的作用非常单纯，就是限制元素的范围\n```cpp\nstd::cout \u003c\u003c std::clamp(-10, 0, 100) \u003c\u003c '\\n';//0\nstd::cout \u003c\u003c std::clamp(200, 0, 100) \u003c\u003c '\\n';//100\n```\n\n\u003cbr\u003e\n\n### [6.8`std::sample`采集样本数据集](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.8sample%E9%87%87%E9%9B%86%E6%A0%B7%E6%9C%AC%E6%95%B0%E6%8D%AE%E9%9B%86.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003crandom\u003e\n#include\u003carray\u003e\n\nint iround(const double\u0026 d) {\n\treturn static_cast\u003cint\u003e(std::round(d));//四舍五入返回double，进行显式类型转换\n}\n\nint main() {\n\tconstexpr size_t n_data{ 200000 };//数据大小\n\tconstexpr size_t n_samples{ 500 };//样本容器大小\n\tconstexpr int mean{ 0 };//均值\n\tconstexpr size_t dev{ 3 };//方差\n\t\n\tstd::random_device rd;//提供对硬件随机数生成器的访问\n\tstd::mt19937 rng{ rd() };//随机数生成器\n\tstd::normal_distribution\u003c\u003edist{ mean,dev };//正态分布的两个参数 均值与方差\n\tstd::array\u003cint, n_data\u003edata{};\n\tfor (auto\u0026 e : data)e = iround(dist(rng));\n\n\tstd::array\u003cint, n_samples\u003esamples{};\n\tstd::sample(data.begin(), data.end(), samples.begin(), n_samples, rng);\n\n\tstd::map\u003cint, size_t\u003e hist{};\n\tfor (const int i : samples) ++hist[i];\n\n\tconstexpr size_t scale{ 3 };\n\tprint(\"{:\u003e3} {:\u003e5} {:\u003c}/{}\\n\", \"n\", \"count\", \"graph\", \"scale\");\n\tfor (const auto\u0026 [value, count] : hist) {\n\t\tprint(\"{:\u003e3} ({:\u003e3}) {}\\n\", value, count, std::string(count , '*'));\n\t}\n}\n```\n\n运行结果:\n\n\t  n count graph/scale\n\t-11 (  1) *\n\t-10 (  1) *\n\t -9 (  1) *\n\t -8 (  1) *\n\t -7 (  5) *****\n\t -6 (  9) *********\n\t -5 ( 11) ***********\n\t -4 ( 30) ******************************\n\t -3 ( 35) ***********************************\n\t -2 ( 62) **************************************************************\n\t -1 ( 70) **********************************************************************\n\t  0 ( 56) ********************************************************\n\t  1 ( 60) ************************************************************\n\t  2 ( 59) ***********************************************************\n\t  3 ( 34) **********************************\n\t  4 ( 29) *****************************\n\t  5 ( 19) *******************\n\t  6 ( 10) **********\n\t  7 (  5) *****\n\t  8 (  2) **\n\n这个内容我们慢慢看。首先看函数`iround`，它的作用十分简单，因为标准库没有给我们提供返回`int`的四舍五入的函数，所以自己稍微包装一下，在里面使用`static_cast\u003cint\u003e`转换后再返回，看着会舒服点。\n\n进入`main`函数，我们定义了几个 **`constexpr`** 的常量，作用也都有注释:**数据大小、样本容器大小、均值、方差**。\n\n下面两行代码就是之前在 **[6.4sort排序容器元素](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.4sort%E6%8E%92%E5%BA%8F%E5%AE%B9%E5%99%A8%E5%85%83%E7%B4%A0.cpp)** 提起过的内容，随机数。\n\n[`std::normal_distribution\u003c\u003edist{ mean,dev };`](https://zh.cppreference.com/w/cpp/numeric/random/normal_distribution)需要注意，这个正态分布算本节重点，接受两个参数**均值与方差**，你可以简单理解我我们这节使用的随机数将以正态分布的方式随机，并且我们会将它的值打印出来直方图。\n\n后面自然而然的就是为`std::array`对象遍历元素赋值按照正态分布的随机值了。\n\n然后我们就要开始抽取样本了: 此时就要再创造一个数组对象，然后使用[**`std::sample`**](https://zh.cppreference.com/w/cpp/algorithm/sample)算法，前面两个参数是被抽取样本的范围，第三个参数是写入样本的输出迭代器，第四个参数是要抽取样本数`n_samples`，第五个参数是用作随机源的随机数生成器`rng`\n\n后面就是简简单单的遍历一下容器映射到`map`中，对其进行打印即可\n\n\u003cbr\u003e\n\n### [6.9生成有序数据序列](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.9%E7%94%9F%E6%88%90%E6%9C%89%E5%BA%8F%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003calgorithm\u003e\n\nint main() {\n\tstd::vector\u003cstd::string\u003evs{ \"dog\",\"cat\",\"velociraptor\" };   \n\tstd::sort(vs.begin(), vs.end());\n\tdo {\n\t\tprintc(vs);\n\t} while (std::next_permutation(vs.begin(), vs.end()));\n\n\tstd::vector\u003cint\u003ev{ 1,2,3 };\n\tdo\n\t{\n\t\tprintc(v);\n\t} while (std::next_permutation(v.begin(),v.end()));\n\t\n\tprint(\"从大到小:\\n\");\n\tstd::sort(v.begin(), v.end(), std::greater\u003cint\u003e{});\n\tdo\n\t{\n\t\tprintc(v);\n\t} while (std::next_permutation(v.begin(), v.end()));\n}\n```\n\n运行结果:\n\n\t[cat] [dog] [velociraptor]\n\t[cat] [velociraptor] [dog]\n\t[dog] [cat] [velociraptor]\n\t[dog] [velociraptor] [cat]\n\t[velociraptor] [cat] [dog]\n\t[velociraptor] [dog] [cat]\n\t[1] [2] [3]\n\t[1] [3] [2]\n\t[2] [1] [3]\n\t[2] [3] [1]\n\t[3] [1] [2]\n\t[3] [2] [1]\n\t从大到小:\n\t[3] [2] [1]\n\n这个例子应该是非常的简短了，我们看到，先是创建了一个`std::vector\u003cstd::string\u003e`的对象并且初始化，然后先对其进行默认的降序排序。\n\n进入`do while`它的条件语句里是执行 [**`std::next_permutation`**](https://zh.cppreference.com/w/cpp/algorithm/next_permutation)，若新排列按字典序大于旧者则为 **true** 。若抵达最后重排并重置范围为首个排列则为 **false**。我们可以看数字的这个，它一开始是`1 2 3 `重新排列后是 `1 3 2 `按照字典序，肯定是大于旧的，所以返回`true`，循环会继续,直到`3 2 1`它是按照首个排列的，也就是从大到小，所以是 **false**，循环结束。\n\n其实我们看最后一段代码可以发现，它直接打印了一次`[3] [2] [1]`就结束了，因为这三个元素的最后一个字符是字典顺序的最后一个\n\n\u003cbr\u003e\n\n### [6.10合并已排序容器](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/6.9%E7%94%9F%E6%88%90%E6%9C%89%E5%BA%8F%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cvector\u003e\n#include\u003calgorithm\u003e\n\nint main() {\n\tstd::vector\u003cstd::string\u003evs1{ \"dog\",\"cat\",\"veloiraptor\" };\n\tstd::vector\u003cstd::string\u003evs2{ \"kirk\",\"sulu\",\"spock\" };\n\tstd::vector\u003cstd::string\u003edest{};\n\tprintc(vs1, \"vs1\");\n\tprintc(vs2, \"vs2\");\n\n\tstd::ranges::sort(vs1);\n\tstd::ranges::sort(vs2);\n\tprintc(vs1, \"vs1\");\n\tprintc(vs2, \"vs2\");\n\n\tstd::merge(vs1.begin(), vs1.end(), vs2.begin(), vs2.end(), std::back_inserter(dest));\n\tprintc(dest, \"dest\");\n}\n\n```\n\n运行结果:\n\n\tvs1: [dog] [cat] [veloiraptor]\n\tvs2: [kirk] [sulu] [spock]\n\tvs1: [cat] [dog] [veloiraptor]\n\tvs2: [kirk] [spock] [sulu]\n\tdest: [cat] [dog] [kirk] [spock] [sulu] [veloiraptor]\n\n[**`std::merge`**](https://zh.cppreference.com/w/cpp/algorithm/merge)算法接受两个已排序的序列，并创建第三个已合并并排序的序列\n\n前面四个参数表示两个输入范围，第五个参数表示结果序列发送的输出迭代器\n\n### 第六章总结\n第六章的内容普遍比较简短简单，都是一些库的使用，如果你是第一次见，那最好还是自己写一下多用用\n\n---\n\n\u003cbr\u003e\n\n## 第七章 字符串、流和格式化\n\nSTL 字符串类是一个功能强大的全功能工具，用于存储、操作和显示基于字符的数据。在高级脚本语言中，可以找到的许多字符串相关的便利、快速和敏捷的功能。\n\n**`std::string`** 类基于 [**`std::basic_string`**](https://zh.cppreference.com/w/cpp/string/basic_string)，这是一个连续的容器类，可以用字符类型实例化。其类签名是这样\n\n```cpp\ntemplate \u003cclass _Elem, class _Traits = char_traits\u003c_Elem\u003e, class _Alloc = allocator\u003c_Elem\u003e\u003e\nclass basic_string\n```\n`Trait` 和 `Allocator` 模板参数通常保留默认值。\n\n`basic_string` 的底层存储是一个连续的 CharT 序列，可以通过 `data()` 成员函数访问:\n```cpp\n#include\u003cstring\u003e\n#include\u003ciostream\u003e\n\nint main() {\n\tconst std::basic_string\u003cchar\u003es{ \"hello\" };\n\tconst char* sdata = s.data();\n\tfor (size_t i = 0; i \u003c s.size(); i++){\n\t\tstd::cout \u003c\u003c sdata[i] \u003c\u003c ' ';\n\t}\n\tstd::cout \u003c\u003c '\\n';\n}\n```\n\n运行结果:\n\n\th e l l o\n\n`data()` 成员函数返回一个指向底层字符数组的 `CharT*`。从 C++11 起，data() 返回的数组以空结\n束，使得 `data()` **等价于** `c_str()`。\n\n`basic_string` 类包含许多在其他连续存储类中可以找到的方法，包括 `insert()`、`erase()`、`push_back()`、\n`pop_back()` 等，这些方法可以操作底层的 CharT 数组。\n\n`std::string` 是 `std::basic_string\u003cchar\u003e` 类型的别名:\n\n```cpp\nusing string  = basic_string\u003cchar, char_traits\u003cchar\u003e, allocator\u003cchar\u003e\u003e;\n```\n\n\u003cbr\u003e\n\n### [7.3轻量字符串对象`string_view`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/7.3%E8%BD%BB%E9%87%8F%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AF%B9%E8%B1%A1string_view.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cstring\u003e\nusing namespace std::literals;\n\nstd::string_view sv() {\n\tconst char text[]{ \"hello\" };\n\tstd::string_view greeting{ text };\n\treturn greeting;\n}\n\nvoid f(const std::string\u0026 str) {\n\n}\nvoid f2(std::string_view str) {\n\n}\n\nint main() {\n\tchar str[10]{ \"hello\" };\n\tstd::string str2{ str };\n\tprint(\"{}\\n\", str2);\n\tstr[0] = 'a';\n\tprint(\"{}\\n\", str2);\n\n\tstd::string_view sview{ str };\n\tprint(\"{}\\n\", sview);\n\tstr[0] = 'b';\n\tprint(\"{}\\n\", sview);\n\n\tauto t = sv();\n\tprint(\"{}\\n\", t);\n\n\tprint(\"{}\\n\", \"hello\"sv.substr(1,4));\n\n\tconstexpr std::string_view str3{ \"哈哈\" };\n\t//constexpr std::string str4{ \"哈哈\" };//error\n\n\tprint(\"{}\\n\", str3);\n\n\tstd::string str4{ \"1\" };\n\tconst std::string str5{ \"1\" };\n\tf(str4);\n\tf(str5);\n\tf(\"1\");//开销大，需要构造临时的std::string对象\n\n\tf2(\"1\");\n\tf2(str4);\n\tf2(str5);\n}\n```\n\n[**`std::string_view`**](https://zh.cppreference.com/w/cpp/string/basic_string_view)是C++17添加的一个字符串视图类，它的构成和原理也十分简单\n\n它的构造函数只是把自己的数据成员`const pointer`以及`size`初始化而已，这是通常的实现，也就是自己不存储任何数据，副本，只是**视图**，依靠指针进行一切访问操作，不提供修改操作\n\n\u003cbr\u003e\n\n### [7.4连接字符串](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/7.4%E8%BF%9E%E6%8E%A5%E5%AD%97%E7%AC%A6%E4%B8%B2.cpp)\n```cpp\n#include\"print.h\"\n#include\u003csstream\u003e\n#include\u003costream\u003e\n#include\u003cchrono\u003e\nusing std::chrono::high_resolution_clock;\nusing std::chrono::duration;\n\nvoid timer(auto(f)()-\u003estd::string) {\n\tauto t1 = high_resolution_clock::now();\n\tstd::string s{ f() };\n\tauto t2 = high_resolution_clock::now();\n\tduration\u003cdouble, std::milli\u003ems = t2 - t1;\n\tprint(\"{}\", s);\n\tprint(\"duration: {} ms\\n\", ms.count());\n}\n\nstd::string concat_string() {\n\tprint(\"concat_string\\n\");\n\tstd::string a{ \"a\" };\n\tstd::string b{ \"b\" };\n\tlong n{};\n\twhile (++n) {\n\t\tstd::string x{};\n\t\tx += a + \", \" + b + \"\\n\";\n\t\tif (n \u003e= 10000000)return x;\n\t}\n\treturn \"error\\n\";\n}\n\nstd::string append_string() {\n\tprint(\"append_string\\n\");\n\tstd::string a{ \"a\" };\n\tstd::string b{ \"b\" };\n\tlong n{};\n\twhile (++n) {\n\t\tstd::string x{};\n\t\tx.append(a);\n\t\tx.append(\", \");\n\t\tx.append(b);\n\t\tx.append(\"\\n\");\n\t\tif (n \u003e= 10000000)return x;\n\t}\n\treturn \"error\\n\";\n}\n\nstd::string concat_ostringstream() {\n\tprint(\"ostringstream\\n\");\n\tstd::string a{ \"a\" };\n\tstd::string b{ \"b\" };\n\tlong n{};\n\twhile (++n) {\n\t\tstd::stringstream x{};\n\t\tx \u003c\u003c a \u003c\u003c \", \" \u003c\u003c b \u003c\u003c \"\\n\";\n\t\tif (n \u003e= 10000000)return x.str();\n\t}\n\treturn \"error\\n\";\n}\n\nstd::string concat_format() {\n\tprint(\"append_format\\n\");\n\tstd::string a{ \"a\" };\n\tstd::string b{ \"b\" };\n\tlong n{};\n\twhile (++n) {\n\t\tstd::string x{};\n\t\tx += std::format(\"{}, {}\\n\", a, b);\n\t\tif (n \u003e= 10000000)return x;\n\t}\n\treturn \"error\\n\";\n}\n\nint main() {\n\ttimer(append_string);\n\ttimer(concat_string);\n\ttimer(concat_ostringstream);\n\ttimer(concat_format);\n}\n```\n\n运行结果:\n\n\tappend_string\n\ta, b\n\tduration: 5285.7537 ms\n\tconcat_string\n\ta, b\n\tduration: 19286.9228 ms\n\tostringstream\n\ta, b\n\tduration: 21790.0884 ms\n\tappend_format\n\ta, b\n\tduration: 29601.7629 ms\n\n\u003cbr\u003e\n\n### [7.5转换字符串](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/7.5%E8%BD%AC%E6%8D%A2%E5%AD%97%E7%AC%A6%E4%B8%B2.cpp)\n```cpp\n#include\"print.h\"\n\nchar char_upper(const char\u0026 c) {\n\treturn static_cast\u003cchar\u003e(std::toupper(c));\n}\nchar char_lower(const char\u0026 c) {\n\treturn static_cast\u003cchar\u003e(std::tolower(c));\n}\nchar rot13(const char\u0026 x) {\n\tauto rot13a = [](char x, char a)-\u003echar {\n\t\treturn a + (x - a + 13) % 26;\n\t};\n\tif (x \u003e= 'A' \u0026\u0026 x \u003c= 'Z')return rot13a(x, 'A');\n\tif (x \u003e= 'a' \u0026\u0026 x \u003c= 'z')return rot13a(x, 'a');\n\treturn x;\n}\nstd::string title_case(std::string\u0026 s) {\n\tauto begin{ s.begin() };\n\tauto end{ s.end() };\n\t*begin++ = char_upper(*begin);\n\tbool space_flag{ false };\n\tfor (auto it{ begin }; it != end; ++it) {\n\t\tif (*it == ' ')space_flag = true;\n\t\telse {\n\t\t\tif (space_flag)*it = char_upper(*it);\n\t\t\tspace_flag = false;\n\t\t}\n\t}\n\treturn s;\n}\n\nint main() {\n\tstd::string s{ \"hello jimi\\n\" };\n\tprint(\"{}\", s);\n\tstd::transform(s.begin(), s.end(), s.begin(), char_upper);\n\tprint(\"{}\", s);\n\tfor (auto\u0026 c : s)c = rot13(c);\n\tprint(\"{}\", s);\n\tfor (auto\u0026 c : s)c = rot13(char_lower(c));\n\tprint(\"{}\", s);\n\n\ttitle_case(s);\n\tprint(\"{}\", s);\n}\n```\n\n运行结果:\n\n\thello jimi\n\tHELLO JIMI\n\tURYYB WVZV\n\thello jimi\n\tHello Jimi\n\n\u003cbr\u003e\n\n### [7.6使用格式库格式化文本](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/7.6%E4%BD%BF%E7%94%A8%E6%A0%BC%E5%BC%8F%E5%BA%93%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%96%87%E6%9C%AC.cpp)\n```cpp\n#include\"print.h\"\n#include\u003cnumbers\u003e\n#include\u003cvector\u003e\n#include\u003calgorithm\u003e\n\ntemplate\u003ctypename T\u003e\nstruct Frac {\n\tT n;\n\tT d;\n};\ntemplate\u003ctypename T\u003e//十分简略，不支持很多操作\nstruct std::formatter\u003cFrac\u003cT\u003e\u003e {\n\ttemplate\u003ctypename ParseContext\u003e\n\tconstexpr auto parse(ParseContext\u0026 ctx) {\n\t\treturn ctx.begin();\n\t}\n\ttemplate\u003ctypename FormatContext\u003e\n\tconstexpr auto format(const Frac\u003cT\u003e\u0026 f, FormatContext\u0026 ctx)const {\n\t\treturn std::format_to(ctx.out(), \"{0:d}/{1:d}\", f.n, f.d);\n\t}\n};\n\ntemplate\u003ctypename T\u003e\nstruct Frac2 {\n\tT n;\n\tT d;\n};\ntemplate\u003ctypename T\u003e\nstruct std::formatter\u003cFrac2\u003cT\u003e\u003e {\n\tconstexpr auto parse(auto\u0026 ctx) {\n\t\tm_fmt[m_buffer_len++] = '{';\n\t\tauto iter = ctx.begin();\n\t\tif (iter == ctx.end() || *iter == '}') {\n\t\t\tm_fmt[m_buffer_len++] = '}';\n\t\t\treturn iter;\n\t\t}\n\t\tm_fmt[m_buffer_len++] = ':';\n\t\tfor (; iter != ctx.end() \u0026\u0026 *iter != '}'; ++iter)\n\t\t\tm_fmt[m_buffer_len++] = *iter;\n\t\tm_fmt[m_buffer_len++] = '}';\n\t\treturn iter;\n\t}\n\tconstexpr auto format(const Frac2\u003cT\u003e\u0026 f, auto\u0026 ctx) {\n\t\tstd::string fmt{};\n\t\tfmt += m_fmt, fmt += \"/\", fmt += m_fmt;\n\t\tauto iter = std::vformat_to(ctx.out(), fmt, std::make_format_args(f.n,f.d));\n\t\treturn iter;\n\t}\nprivate:\n\tchar m_fmt[16]{};\n\tsize_t m_buffer_len = 0;\n};\n\nint main() {\n\tconst int a{ 47 };\n\tconst char* human{ \"earthlings\" };\n\tconst std::string_view alien{ \"vulacans\" };\n\tconst double df_pi{ std::numbers::pi };\n\tconst int inta{ 47 };\n\n\tprint(\"Hex: {0:x} Octal: {0:o} Decunak {0:d}\\n\", a);\n\tprint(\"Hello {1} we are {0}\\n\", human, alien);\n\n\tprint(\"π is {}\\n\", df_pi);\n\tprint(\"π is {:.5}\\n\", df_pi);\n\n\tprint(\"inta is [{:10}]\\n\", inta);\n\tprint(\"inta is [{:\u003c10}]\\n\", inta);\n\tprint(\"inta is [{:\u003e10}]\\n\", inta);\n\n\tprint(\"inta is [{:*\u003c10}]\\n\", inta);\n\tprint(\"inta is [{:0\u003e10}]\\n\", inta);\n\n\tprint(\"inta is [{:^10}]\\n\", inta);\n\tprint(\"inta is [{:_^10}]\\n\", inta);\n\n\tprint(\"{:\u003e8}: [{:04x}]\\n\", \"Hex\", inta);\n\tprint(\"{:\u003e8}: [{:4o}]\\n\", \"Octal\", inta);\n\tprint(\"{:\u003e8}: [{:4d}]\\n\", \"Decimal\", inta);\n\n\tFrac\u003clong\u003en{ 3,5 };\n\tprint(\"{}\\n\", n);\n\t//print(\"{:0x}\\n\", n);//error，因为我们的特化过于简单\n\tFrac2\u003clong\u003en2{ 10,5 };\n\tprint(\"{:0x}\\n\", n2);\n\n\tint array[] = { 1,2,3,4,5,6 };\n\tstd::vector v = { 122, 1222, 1222 };\n\tprint(\"{:0x}\\n\", v);\n\tprint(\"{:0x}\\n\", array);\n}\n```\n\n运行结果:\n\n\tHex: 2f Octal: 57 Decunak 47\n\tHello vulacans we are earthlings\n\tπ is 3.141592653589793\n\tπ is 3.1416\n\tinta is [        47]\n\tinta is [47        ]\n\tinta is [        47]\n\tinta is [47********]\n\tinta is [0000000047]\n\tinta is [    47    ]\n\tinta is [____47____]\n\t     Hex: [002f]\n\t   Octal: [  57]\n\t Decimal: [  47]\n\t3/5\n\ta/5\n\t[7a, 4c6, 4c6]\n\t[1, 2, 3, 4, 5, 6]\n\n我相信你也注意到了，这一块的内容不少是在`1.2`讲过，书上唯一重新弄的也就是这个特化，不再是普通类类型，而是**模板类**，这是一个偏特化，但是如果只是这样，那也就不值得讲什么了。\n\n我们修改了[**`print.h`**](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/print.h)头文件，往里面添加了一个偏特化，并且我们写了一个`Frac2`的对`std::formatter`的特化�","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmq-b%2Fcpp20-stl-cookbook-src","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmq-b%2Fcpp20-stl-cookbook-src","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmq-b%2Fcpp20-stl-cookbook-src/lists"}