# Cpp20-STL-Cookbook-src
**Repository Path**: Mq-b/Cpp20-STL-Cookbook-src
## Basic Information
- **Project Name**: Cpp20-STL-Cookbook-src
- **Description**: C++20 STL Cookbook
- **Primary Language**: C++
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2023-12-25
- **Last Updated**: 2025-02-05
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 《C++20 STL Cookbook》2023
### 环配置
**Visual Studio Enterprise 2022(64位) 版本 17.4.3**
**CMake 3.8**
**R7 5700U** 八核心十六线程
### [B站视频讲解](https://www.bilibili.com/video/BV1r8411N75b/?spm_id_from=333.999.0.0&vd_source=1992ca910d6cd0582931f6f985dc7fa0)
- [《C++20 STL Cookbook》2023](#c20-stl-cookbook2023)
- [环境](#环境)
- [B站视频讲解](#b站视频讲解)
- [第一章 C++20的新特性](#第一章-c20的新特性)
- [1.2格式化文本](#12格式化文本)
- [1.3使用编译时constexpr `std::vector`和`std::string`](#13使用编译时constexpr-stdvector和stdstring)
- [1.4安全比较不同类型的整数`cmp_less`](#14安全比较不同类型的整数cmp_less)
- [1.5三路比较运算符](#15三路比较运算符)
- [1.6查找特性测试宏](#16查找特性测试宏)
- [1.7概念(`concept`)和约束(`constraint`)-创建更安全的模板](#17概念concept和约束constraint-创建更安全的模板)
- [1.8模块](#18模块)
- [1.9视图](#19视图)
- [第一章总结](#第一章总结)
- [第二章 STL的泛型特性](#第二章-stl的泛型特性)
- [2.2`std::span`类](#22span类)
- [2.3结构化绑定](#23结构化绑定)
- [2.4`if`\&`switch`中的初始化](#24ifswitch中的初始化)
- [2.5模板参数推导(CTAD)](#25模板参数推导ctad)
- [2.6编译期`if`](#26编译期if)
- [第二章总结](#第二章总结)
- [第三章 STL容器](#第三章-stl容器)
- [3.3使用擦除函数从容器中擦除项](#33使用擦除函数从容器中擦除项)
- [3.4常数时间内从未排序的向量中删除项](#34常数时间内从未排序的向量中删除项)
- [3.5安全的访问`std::vector`元素](#35安全的访问stdvector元素)
- [3.6保持`std::vector`元素的顺序](#36保持stdvector元素的顺序)
- [3.7高效的将元素插入到`std::map`中](#37高效的将元素插入到stdmap中)
- [3.8高效的修改`std::map`项的键值](#38高效的修改stdmap项的键值)
- [3.9自定义键值的`std::unordered_map`](#39自定义键值的stdunordered_map)
- [3.10使用`std::set`进行输入和筛选](#310使用set进行输入和筛选)
- [3.11实现简单的RPN计算器与`std::deque`](#311实现简单的rpn计算器与deque)
- [3.12使用`std::map`的词频计数器](#312使用stdmap的词频计数器)
- [3.13找出含有相应长句的`std::vector`](#313找出含有相应长句的stdvector)
- [3.14使用`std::multimap`制作待办事项](#314使用stdmultimap制作待办事项)
- [第三章总结](#第三章总结)
- [第四章 兼容迭代器](#第四章-兼容迭代器)
- [4.3创建可迭代范围](#43创建可迭代范围)
- [4.4使迭代器与STL迭代器特性兼容](#44使迭代器与stl迭代器特性兼容)
- [4.5使用迭代器适配器填充STL容器](#45使用迭代器适配器填充stl容器)
- [4.6创建一个迭代器生成器](#46创建一个迭代器生成器)
- [4.7反向迭代器的反向适配器](#47反向迭代器的反向适配器)
- [4.8用哨兵迭代未知长度的对象](#48用哨兵迭代未知长度的对象)
- [4.9构建zip迭代器适配器](#49构建zip迭代器适配器)
- [4.10创建随机访问迭代器](#410创建随机访问迭代器)
- [第四章总结](#第四章总结)
- [第五章 lambda表达式](#第五章-lambda表达式)
- [5.3用于作用域可重用代码](#53用于作用域可重用代码)
- [5.4算法库中作为谓词](#54算法库中作为谓词)
- [5.5与`std::function`一起作为多态包装器](#55与stdfunction一起作为多态包装器)
- [5.6用递归连接lambda](#56用递归连接lambda)
- [5.7将谓词与逻辑连接词连接起来](#57将谓词与逻辑连接词连接起来)
- [5.8用相同的输入调用多个lambda](#58用相同的输入调用多个lambda)
- [5.9对跳转表使用映射lambda](#59对跳转表使用映射lambda)
- [第五章总结](#第五章总结)
- [第六章 STL算法](#第六章-stl算法)
- [6.2基于迭代器的复制](#62基于迭代器的复制)
- [6.3将容器元素连接到以供字符串当中](#63将容器元素连接到以供字符串当中)
- [6.4`std::sort`排序容器元素](#64stdsort排序容器元素)
- [6.5`std::transform`修改容器内容](#65stdtransform修改容器内容)
- [6.6查找特定项](#66查找特定项)
- [6.7将容器元素限制在`std::clamp`范围内](#67将容器元素限制在stdclamp范围内)
- [6.8`std::sample`采集样本数据集](#68stdsample采集样本数据集)
- [6.9生成有序数据序列](#69生成有序数据序列)
- [6.10合并已排序容器](#610合并已排序容器)
- [第六章总结](#第六章总结)
- [第七章 字符串、流和格式化](#第七章-字符串流和格式化)
- [7.3轻量字符串对象`std::string_view`](#73轻量字符串对象string_view)
- [7.4连接字符串](#74连接字符串)
- [7.5转换字符串](#75转换字符串)
- [7.6使用格式库格式化文本](#76使用格式库格式化文本)
- [7.7删除字符串的空白](#77删除字符串的空白)
- [7.8从用户输入中读取字符串](#78从用户输入中读取字符串)
- [7.9统计文件中的单词数](#79统计文件中的单词数)
- [7.10使用文件输入初始化复杂结构体](#710使用文件输入初始化复杂结构体)
- [7.11使用`char_traits`](#711使用char_traits)
- [7.12用正则表达式解析字符串](#712用正则表达式解析字符串)
- [第七章总结](#第七章总结)
- [第八章 实用工具类](#第八章-实用工具类)
- [8.2`std::optional`管理可选值](#82-stdoptional-管理可选值)
- [8.3`std::any`保证类型安全](#83any保证类型安全)
- [8.4`std::variant`存储不同的类型](#84-stdvariant-存储不同的类型)
- [8.5`std::chrono`的时间事件](#85chrono的时间事件)
- [8.6对元组使用折叠表达式](#86对元组使用折叠表达式)
- [8.7`std::unique_ptr`管理已分配内存](#87-stdunique_ptr-管理已分配内存)
- [8.8`std::shared_ptr`的共享对象](#88-stdshared_ptr-的共享对象)
- [8.9对共享对象使用弱指针](#89对共享对象使用弱指针)
- [8.10共享管理对象的成员](#810共享管理对象的成员)
- [8.11比较随机数引擎](#811比较随机数引擎)
- [8.12比较随机数分布发生器](#812比较随机数分布发生器)
- [第九章总结](#第九章-并发和并行)
- [第九章 并发和并行](#第九章-并发和并行)
- [9.2休眠一定时间](#92休眠一定时间)
- [9.3`std::thread`实现并发](#93-stdthread-实现并发)
- [9.4`std::async`实现并发](#94-stdasync-实现并发)
- [9.5`STL`算法与执行策略](#95stl算法与执行策略)
- [9.6互斥锁和锁安全的共享数据](#96互斥锁和锁安全的共享数据)
- [9.7`std::atomic`共享标志和值](#97stdatomic共享标志和值)
- [9.8`std::call_once`初始化线程](#98stdcall_once初始化线程)
- [9.9`std::condition_variable`解决生产者-消费者问题](#99stdcondition_variable解决生产者-消费者问题)
- [9.10实现多个生产者和消费者](#910实现多个生产者和消费者)
- [第十章 文件系统](#第十章-文件系统)
- [10.2为`path`类特化`formatter`](#102为path类特化formatter)
- [10.3使用带有路径的操作函数](#103使用带有路径的操作函数)
- [10.4列出目录中的文件](#104列出目录中的文件)
- [10.5使用`grep`实用程序搜索目录和文件](#105使用grep实用程序搜索目录和文件)
- [10.6使用`regex`和`directory_iterator`重命名文件](#106使用regex和directory_iterator重命名文件)
- [10.7创建磁盘使用计数器](#107创建磁盘使用计数器)
## 第一章 C++20的新特性
### [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)
```cpp
#include
#include
#include
#include
template < typename... Args>
void print(const std::string_view fmt_str, Args&&... args) {
auto fmt_args{ std::make_format_args(args...) };
std::string outstr{ std::vformat(fmt_str, fmt_args) };
fputs(outstr.c_str(), stdout);
}
struct Frac {
int a, b;
};
template<>
struct std::formatter {
template
constexpr auto parse(ParseContext& ctx) {
return ctx.begin();
}
template
auto format(const Frac& f, FormatContext& ctx)const {
return std::format_to(ctx.out(), "{0:d}/{1:d}", f.a, f.b);
}
};
int main() {
Frac f{ 1,10 };
print("{}", f);
}
//特化规则参见: https://zh.cppreference.com/w/cpp/named_req/Formatter
```
运行结果:
1/10
你可以把这个内容分为两个部分:
1. 实现模板函数 **`print`**
使用与`std::format()`函数相同的参数。第一个参数是格式字符串的 `std::string_view` 对象,后面作为参数 的可变参数包。
[`std::make_format_args()`](https://zh.cppreference.com/w/cpp/utility/format/make_format_args) 函数的作用: *接受参数包并返回一个对象,该对象包含适合格式化的已擦除 类型的值。*
`fmt_str`就是传递的格式化字符串,`fmt_args`是一个保有格式化参数的对象,使用[`std::vformat(fmt_str, fmt_args)`](https://zh.cppreference.com/w/cpp/utility/format/vformat)即可返回格式化完毕的字符串。我们使用 `fputs()` 将值输出到控制台上 (这比 `cout` 高效得多)。
2. [**`std::formatter`**](https://zh.cppreference.com/w/cpp/utility/format/formatter) 特化
对于自定义,或者说标准没有对其有特化的类型,需要我们自行特化`std::formatter`才可以正确的格式化。
**`parse() `** 函数解析格式字符串,从冒号之后 (若没有冒号,则在开大括号之后) 直到但不包括结 束大括号 (就是指定对象类型的部分)。其接受一个 `ParseContext `对象,**并返回一个迭代器**。这里,可以只返回 `begin()` 迭代器。因为我们的类型不需要新语法,所以无需准备任何东西。
**`format()`** 函数接受一个 `Frac` 对象和一个 `FormatContext` 对象,**返回结束迭代器**。**`format_to()`** 函数可使这变得很容易,**其可以接受一个迭代器、一个格式字符串和一个参数包**。本例中,参数包是 Frac 类的两个属性,分子和分母。 需要做的就是提供一个简单的格式字符串`“{0}/{1}”`以及分子和分母的值 (0 和 1 表示参数的 位置)。
### [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)
```cpp
#include
#include
constexpr auto f() {
std::vectorv{ 1,2,3 };
return v;
}
constexpr auto f2() {
int* p = new int{ 10 };
//未delete解除分配
return *p;
}
int main() {
constexpr auto n = f().size();//√
//constexpr auto n2 = f()//error
//constexpr auto n3 = f2()//error
}
```
`C++20` 允许在新的上下文中使用 [**`constexpr`**](https://zh.cppreference.com/w/cpp/language/constexpr),这些语句可以在编译时计算,从而提高了效率(此关键字自`c++11`诞生,一直在增加和改进,我们不再强调)。
其中包括在 `constexpr` 上下文中使用 [`string`](https://zh.cppreference.com/w/cpp/string/basic_string) 和 [`vector`](https://zh.cppreference.com/w/cpp/container/vector) 对象的能力。所以 **,这些对象本身可能不声 明为 constexpr**,**但可以在编译时上下文中使用。**
```cpp
constexpr void f() {
constexpr std::string s{ "乐" };
}//错误
constexpr void f() {
std::string s{ "乐" };
}//正确
```
也可以在`constexpr`上下文中使用算法:
```cpp
constexpr int use_vector() {
std::vector vec{ 1, 2, 3, 4, 5 };
return accumulate(begin(vec), end(vec), 0);
}
int main() {
constexpr int ret = use_vector();
}
```
`C++20` 开始,标准 `string` 和 `vector` 类具有`constexpr`限定的构造函数和析构函数,这是可在编译时使用的
前提。所以,分配给 `string` 或 `vector` 对象的内存,也必须在编译时释放。
例如,`constexpr` 函数返回一个 `vector`,编译时不会出错(但是实测 **`gcc msvc clang`** 全部编译错误):
```cpp
constexpr auto f() {
std::vectorv{ 1,2,3 };
return v;
}
int main() {
constexpr auto ret = f();//error
}
```
在编译期间分配和释放了 `vector` 对象,该对象在运行时不可用,理论上可以返回通过编译,实际不可,就算可,你返回了也没有
办法去使用。在运行时使用一些 `vector` 对象的适配 `constexpr` 的方法,比如 `size()`,它是`constexpr`限定的。
```cpp
constexpr auto f() {
std::vectorv{ 1,2,3 };
return v;
}
int main(){
constexpr auto n = f();//√
}
```
### [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)
``` cpp
#include
template
constexpr bool cmp_less(T t, U u)noexcept {
using UT = std::make_unsigned_t;//有符号类型到无符号类型的安全转换。
using UU = std::make_unsigned_t;
if constexpr (std::is_signed_v == std::is_signed_v)
return t < u;
else if constexpr (std::is_signed_v)
return t < 0 ? true : UT(t) < u;
else
return u < 0 ? false : t < UU(u);
}
int main() {
std::cout << std::boolalpha << (5u < -1) << '\n';//true
std::cout << std::boolalpha << ::cmp_less(5u, 1) << '\n';//false
std::cout << std::boolalpha << ::cmp_less(5u, 2u) << '\n';//false
}
```
`C++20` 在 [**`utility`**](https://zh.cppreference.com/w/cpp/header/utility) 引入了一组[**比较函数**](https://zh.cppreference.com/w/cpp/utility/intcmp),他们分别是:
- `std::cmp_equal`
- `std::cmp_not_equal`
- `std::cmp_less`
- `std::cmp_greater`
- `std::cmp_less_equal`
- `std::cmp_greater_equal`
如上述[代码](https://zh.cppreference.com/w/cpp/utility/intcmp#.E5.8F.AF.E8.83.BD.E7.9A.84.E5.AE.9E.E7.8E.B0)中的例子一样,它与内建比较运算符不同,**负有符号整数**与**无符号整数**的比较结果始终为小于,且不为等于。
### [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)
``` cpp
#include
struct X {
int a{};
double b{};
char c{};
friend auto operator<=>(const X&,const X&) = default;
};
struct Y {
int a = 6;
};
auto operator<=>(const Y& y, const Y& y2)noexcept->int {//自定义
if (y.a == y2.a)return 0;
if (y.a > y2.a)return 1;
if (y.a < y2.a)return -1;
}
auto operator==(const Y& y, const Y& y2)noexcept->bool {
return y.a == y2.a;
}
int main() {
X x{ 10,1.2,'*' };
X x2{ 10,1,'*' };
x == x2;
x <= x2;
x != x2;
x >= x2;
Y y{ 1 };
Y y2{ 2 };
std::cout << (y <=> y2) << '\n'; //-1
std::cout << std::boolalpha << (y > y2) << '\n';//false
std::cout << std::boolalpha << (y < y2) << '\n';//true
std::cout << std::boolalpha << (y != y2) << '\n';//true
}
//三路比较运算符: https://zh.cppreference.com/w/cpp/language/operator_comparison
//默认比较: https://zh.cppreference.com/w/cpp/language/default_comparisons
```
三路比较运算符表达式的形式为`表达式1 <=> 表达式2`该表达式将返回一个对象
若`表达式1 < 表达式2`,则`(表达式1 <=> 表达式2) < 0`
若`表达式1 > 表达式2`,则`(表达式1 <=> 表达式2) > 0`
若`表达式1 == 表达式2`,则`(表达式1 <=> 表达式2) == 0`
每当`<` `>` `<=` `>=` `<=>`被比较且重载决议选择该重载时,`operator<=>`都会被调用
若`operator<=>`是默认版本且`operator==`完全没有被声明,则`operator==`将隐式采用默认版本
```cpp
struct C
{
int num;
auto operator<=>(const C& c)const = default;
};
int main()
{
C c1{1};
C c2{2};
std::cout << std::boolalpha << (c1 == c2) << '\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)
``` cpp
#include
#include
#ifdef __cpp_lib_three_way_comparison
# include
#else
# error 没有与之对应的头文件
#endif // __cpp_lib_three_way_comparison
#ifdef __cpp_lib_format
# include
#else
# error 没有与之对应的头文件
#endif // __cpp_lib_three_way_comparison
#if __has_include()//检查能不能找到这个文件,如果能找到这个宏就返回1
# include
#endif
int main() {
std::cout << __cpp_lib_three_way_comparison << '\n';//为 201907,意味着其在 2019 年 7 月采纳。
std::cout << __cpp_lib_format << '\n'; //为 202110,意味着其在 2021 年 10 月采纳。
}
//库功能性测试宏: https://zh.cppreference.com/w/cpp/utility/feature_test
//诊断指令: https://zh.cppreference.com/w/cpp/preprocessor/error
```
运行结果:
```
201907
202207
```
除此外,这里我跳转到MSVC上的[`version`](https://en.cppreference.com/w/cpp/header/version)库,我们还可以看到更多的版本宏
```cpp
#define __cpp_lib_jthread 201911L
#define __cpp_lib_latch 201907L
#define __cpp_lib_list_remove_return_type 201806L
#define __cpp_lib_math_constants 201907L
#ifdef __cpp_lib_concepts
#define __cpp_lib_move_iterator_concept 202207L
#endif // __cpp_lib_concepts
#define __cpp_lib_polymorphic_allocator 201902L
#define __cpp_lib_remove_cvref 201711L
#define __cpp_lib_semaphore 201907L
#define __cpp_lib_smart_ptr_for_overwrite 202002L
#ifdef __cpp_consteval
#define __cpp_lib_source_location 201907L
#endif // __cpp_consteval
#define __cpp_lib_span 202002L
#define __cpp_lib_ssize 201902L
#define __cpp_lib_starts_ends_with 201711L
#define __cpp_lib_syncbuf 201803L
#ifdef __cpp_lib_concepts
#define __cpp_lib_three_way_comparison 201907L
#endif // __cpp_lib_concepts
#define __cpp_lib_to_address 201711L
#define __cpp_lib_to_array 201907L
#define __cpp_lib_type_identity 201806L
#define __cpp_lib_unwrap_ref 201811L
#endif // _HAS_CXX20
```
当然,你也可以到[`库特性测试宏 (C++20)`](https://zh.cppreference.com/w/cpp/utility/feature_test)来进行查看
通常,我们使用条件宏增强不同平台下的兼容性:
```cpp
#if defined(__APPLE__)
#define PI 3.14159265358979323846
#elif defined(__linux__)
#define PI 3.141592653589793
#elif defined(_WIN32)
#define PI 3.1415926535897932384626433832795
#endif
#ifdef _WIN32
#include
#define sleep_ms(x) Sleep(x)
#else
#include
#include
#define sleep_ms(x) std::this_thread::sleep_for(std::chrono::milliseconds(x))
#endif
```
[`条件宏`](https://zh.cppreference.com/w/cpp/preprocessor/conditional)至C++23已经有了8种,分别是:
```cpp
#if
#ifdef
#ifndef
#elif
#elifdef // C++23起
#elifndef // C++23起
#else
#endif
```
### [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)
``` cpp
#include
template
void f(T t) {}
template
requires std::integral || std::is_pointer_v
struct X {};
template
requires std::is_integral_v
T n{};
template
concept love = std::is_integral_v && (std::is_same_v || std::is_same_v);
void f2(love auto){}
int main() {
f(1); // 1 是 int,约束满足
f('*'); // '*' 是整数类型(Integer Type)之一,约束满足
//f(1.2);
X x; // int 满足两个约束的析取之一:std::integral,约束满足
//Xx2;
X x3; // double* 满足两个约束的析取之一:std::is_pointer_t,约束满足
n = 3;
//n;
std::cout << n << '\n';
f2(1); // 满足合取 std::is_integral_v 和 std::is_same_v
f2(1u); // 满足合取 std::is_integral_v,std::is_same_v
//f2(1l);
}
//Requires表达式 https://zh.cppreference.com/w/cpp/language/requires
//约束与概念 https://zh.cppreference.com/w/cpp/language/constraints
```
>*约束(Constraint)是对模板形参提出的一种要求,这种要求的具名集合被称为概念(Concept)。每一个概念均为[**谓词**](https://zh.cppreference.com/w/cpp/named_req/Predicate) ,且在**编译期求值**,并在用作约束时成为模板接口的一部分*。
作为 `C++20` 引入的四大新特性之一:`Concept` ,提出了一种比 *SFINAE* 更好的约束方法,它易于理解和编写,也能在出现问题时给出更可读的编译期报错。概念的定义形式如下:
> **_template_** < 模板形参列表 >
> **_concept_** 概念名 属性(可选) **=** 约束表达式 **;**
在上述例子中,概念 `love` 的定义就是这样:
```cpp
template
concept love = std::is_integral_v && (std::is_same_v || std::is_same_v);
```
其中,运算符 `&&` 构成两个约束的合取(*Conjunction*),两个约束均满足时合取满足。`||` 构成的析取(*Disjunction*)则为两者之一满足则析取满足。
`requires` 关键字可用于进行多个约束的分开表达,约束之间的关系均为合取,分为以下多种情况:
- 简单约束
```cpp
// 1. 简单约束
template
concept Addable = requires(T a, T b)
{
a + b; //编译器会检查该表达式是否 "合法"
}
```
- 类型约束
```cpp
template
struct tmp
{
using value = T;
};
template >
struct test {};
template
using Ref = T&;
template
concept Cpt = requires
{
typename T::value; // 检查 T 是否存在成员 T::value
typename X; // 检查是否存在模板类 S 的特化 S
typename Ref; // 检查是否存在合法别名模板 Ref
}
```
- 复合约束
复合约束用于约束表达式的返回类型。其定义为:
> { 表达式 } **noexcept**(可选) -> 类型约束 **;**
例如:
```cpp
template
concept C = requires(T x) {
{x * 2} -> typename T::inner; // 表达式 x * 2 的类型可转换为 T::inner
{x + 3} -> std::same_as; // 表达式 x + 3 需要满足约束 std::same_as
};
```
复合约束的计算顺序为:
- 计算 表达式 是否合法
- 如果有 **noexcept** 限定,则 表达式 不能抛出异常
- `decltype((expression))` 的类型必须满足 类型约束
全满足则结果为 `true` ,否则为 `false`
### [1.8模块](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.8%E6%A8%A1%E5%9D%97.cpp)
`1.8模块.cpp`
``` cpp
import test;
int main() {
/*int n[]{
#include"t.txt"
};
for (auto i : n) {
std::cout << i << ' ';
}*/
std::cout << mylib::add(1, 2) << '\n';
//mylib::print("*");
t();
}
//模块: https://zh.cppreference.com/w/cpp/language/modules
//编译设置:add_executable (Test1 "src/1.8模块.cpp" "src/test.ixx" "src/test2.ixx")
```
[`test.ixx`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/test.ixx)
```cpp
module;
#define PI 3.14
export module test;
export import;
export import test2;
namespace mylib {
export auto add(std::integral auto a, std::integral auto b) {
return a + b;
}
auto print(auto t) {
std::cout << t << '\n';
}
}
```
[`test2.ixx`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/test2.ixx)
```cpp
export module test2;
import;
export void t() {
std::cout << "乐\n";
}
```
[`t.txt`](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/t.txt)
```
1,2,3,4,5
```
### [1.9视图](https://github.com/Mq-b/Cpp20-STL-Cookbook-src/blob/master/src/1.9%E8%A7%86%E5%9B%BE.cpp)
``` cpp
#include
#include
#include
namespace stdv = std::views;
namespace stdr = std::ranges;
void print(stdr::range auto v) {
for (const auto& i : v)std::cout << i << ' ';
endl(std::cout);
}
int main() {
std::vector nums{ 1,2,3,4,5,6,7,8,9,10 };
auto ret = nums | stdv::take(5) | stdv::reverse;
print(ret);
auto ret2 = nums | stdv::filter([](int i) {return i > 6; });
print(ret2);
auto ret3 = nums | stdv::transform([](int i) {return i * i; });
print(ret3);
print(nums);//视图是不会改变原来的数据的
std::vectorstrs{ "🐴","🐭","🥵","🤣" };
auto ret4 = strs | stdv::reverse;
print(ret4);
auto ret5 = nums | stdv::filter([](int i) {return i % 2 != 0; }) | stdv::transform([](int i) {return i * 2; });
print(ret5);
auto nums_ = stdv::iota(1, 10);
print(nums_);
auto rnums = stdv::iota(1) | stdv::take(200);
print(rnums);
stdr::copy(strs | stdv::reverse | stdv::drop(2), std::ostream_iterator(std::cout," "));
}
//范围库: https://zh.cppreference.com/w/cpp/ranges
```
### 第一章总结
第一章的内容需要细看,很多其实书说的并不全面,比如范围,模块,约束与概念,自己注意去看我们提到的之前讲过的视频,以及这些demo
如果你是初学,最好都自己写一下运行,顺便提一下`print.h`后面我们会经常用到这个头文件,我们其实是一步步补充的,但是我们直接把
这个头文件的内容先放出来吧,有不少的打印模板函数
**`print.h`**
```cpp
#pragma once
#include
#include
#include
#include