diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000..245d6bdf --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,29 @@ +name: Check + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Cache + id: cache + uses: actions/cache@v3 + with: + path: check/target/release/check + key: check-build-cache-0001 + + - name: Build + if: steps.cache.outputs.cache-hit != 'true' + run: | + cd check + cargo build --release + + - name: Check + run: check/target/release/check diff --git a/.gitignore b/.gitignore index 0421b3a6..ec2ba719 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ *.out *.pygstyle *.pygtex -*.toc \ No newline at end of file +*.toc +/check/target +/check/Cargo.lock \ No newline at end of file diff --git a/C++CoreGuidelines/README.md b/C++CoreGuidelines/README.md index 992db917..dd0efb4d 100644 --- a/C++CoreGuidelines/README.md +++ b/C++CoreGuidelines/README.md @@ -8,7 +8,7 @@ 它并不适合完全0基础,虽然书籍自称自己的目标读者群是:**所有 C++ 程序员,包括可能考虑使用 C 语言的程序员。** -如果你能在阅读的同时学习现代C++,倒也无所谓了,虽然它涉及到的远不止单纯的语言层面。 +如果你能在阅读的同时学习现代 C++,倒也无所谓了,虽然它涉及到的远不止单纯的语言层面。 同时,本书有很多有意思的 **C++金句**,我们会进行特殊标注(斜体)。比如 diff --git "a/C++CoreGuidelines/\347\254\2543\347\253\240-\346\216\245\345\217\243.md" "b/C++CoreGuidelines/\347\254\2543\347\253\240-\346\216\245\345\217\243.md" index dafc4752..508f7c6f 100644 --- "a/C++CoreGuidelines/\347\254\2543\347\253\240-\346\216\245\345\217\243.md" +++ "b/C++CoreGuidelines/\347\254\2543\347\253\240-\346\216\245\345\217\243.md" @@ -203,11 +203,11 @@ void showRectangle(Point top_left, Point bottom_right); // 好 尽管函数 showRectangle 本应当只显示一个矩形,但修改了它的参数。实质上它有两个目的,因此,它的名字有误导性(I.1)。另外,函数签名没有提供关于参数应该是什么的任何信息,也没有关于应该以什么顺序提供参数的信息(I.23 和 I.24)。此外,参数是没有取值范围约束的双精度浮点数。因此,这种约束必须在函数中确立(I.4)。对比而言,第二个 showRectangle 函数接受两个具体的点对象(Point)。 -- *检查 Point是否合法值是 Point 构造函数的工作。这种检查工作本来就不是函数 showRectangle 的职责*。 +- *检查 Point 是否合法值是 Point 构造函数的工作。这种检查工作本来就不是函数 showRectangle 的职责*。 进一步阐述规则 **I.23** 和 **I.24** 以及标准模板库(STL)中的函数 [**`std::transform_reduce`**](https://zh.cppreference.com/w/cpp/algorithm/transform_reduce)。首先,需要定义属于“[可调用](https://zh.cppreference.com/w/cpp/named_req/Callable)”(callable)。可调用实体是在行为上像函数的东西。它可以是函数,也可以是函数对象,或者是 lambda 表达式。如果可调用实体接受一个参数,它就是一元可调用实体;如果它接受两个参数,则称为二元可调用实体。 -std::transform_reduce 先将一元可调用实体应用到一个范围或将二元可调用实体应用在两个范围,然后将二元可调用实体应用到前一步的结果的范围上。当你使用一个一元 lambda 表达式调用 std::transform_reduce时,这种调用易于正确使用。 +std::transform_reduce 先将一元可调用实体应用到一个范围或将二元可调用实体应用在两个范围,然后将二元可调用实体应用到前一步的结果的范围上。当你使用一个一元 lambda 表达式调用 std::transform_reduce 时,这种调用易于正确使用。 ```cpp std::vectorstrVec{"Only", "for", "testing", "purpose"}; @@ -221,7 +221,7 @@ std::size_t res = std::transform_reduce( ``` > 事实上原书给的上面这段代码是有问题的,无法在 `msvc` 通过编译,这里使用的是 **`0`** 做初始值,有窄化转换,[msvc 使用的是 `{}` 初始化](https://github.com/microsoft/STL/blob/adea8d5ae280cafb91ae69b8dfaecd1c37a847d9/stl/inc/execution#L4235)。,检测到了,于是编译错误。(但是需要注意,不是简单的 `{}` 检测的问题,msvc 的实现和其他 stl 从根本上就不一样) -> 这里其实可以算作是 msvc 的bug,这个场景需要良构 这里应该把 0 换成 `Oull` (基于当前 64 位环境),或者标准够高使用 `0uz`,再或者直接 `std::size_t{0}`。 +> 这里其实可以算作是 msvc 的 bug,这个场景需要良构 这里应该把 0 换成 `Oull` (基于当前 64 位环境),或者标准够高使用 `0uz`,再或者直接 `std::size_t{0}`。 函数 `std::transform_reduce` 先将每个字符串变换为它的长度 `[](std::string s) {return s.size(); }` , 并将二元可调用实体 `[](std::size_t a, std::size_t b) {return a + b; },` 应用到结果的范围上。求和的初始值是 0。整个计算是并行的 `std::execution::par`。 diff --git "a/C++CoreGuidelines/\347\254\2544\347\253\240-\345\207\275\346\225\260.md" "b/C++CoreGuidelines/\347\254\2544\347\253\240-\345\207\275\346\225\260.md" index 44322fce..ea50e7de 100644 --- "a/C++CoreGuidelines/\347\254\2544\347\253\240-\345\207\275\346\225\260.md" +++ "b/C++CoreGuidelines/\347\254\2544\347\253\240-\345\207\275\346\225\260.md" @@ -653,7 +653,7 @@ double* ptr = new double[5]; func(ptr); ``` -关键问题是,谁是资源的所有者?是使用该数组的 func 中的被调用方,还是创建该数组的 func 的调用方?如果 func 是所有者,那么它必须释放该资源。如果不是,则func 不可以释放资源。这种情况不能令人满意。如果 func 不释放资源,可能会发生内存泄露。如果 func 释放了资源,可能会导致未定义行为。 +关键问题是,谁是资源的所有者?是使用该数组的 func 中的被调用方,还是创建该数组的 func 的调用方?如果 func 是所有者,那么它必须释放该资源。如果不是,则 func 不可以释放资源。这种情况不能令人满意。如果 func 不释放资源,可能会发生内存泄露。如果 func 释放了资源,可能会导致未定义行为。 因此,所有权需要记录在文档中。使用现代 C++ 中的类型系统来定义所有权的契约是朝正确方向迈出的一大步,可以消除文档的模糊性。 @@ -1035,7 +1035,7 @@ sum 是一个变参数函数。它的第一个参数是需要被求和的参数 请阅读 cppreference.com 中关于[变参数函数](https://zh.cppreference.com/w/cpp/language/variadic_arguments)的部分来获取进一步的信息。 -代码行(1)和(2)中出了些状况。(1)中参数 num 的数量是错的;(2)中我提供了一个 double 而不是一个int。[输出结果](https://godbolt.org/z/nch8fjdf5)显示了这两个问题。(1)中的最后一个元素丢失了,而 double 被解释为 int(2)。 +代码行(1)和(2)中出了些状况。(1)中参数 num 的数量是错的;(2)中我提供了一个 double 而不是一个 int。[输出结果](https://godbolt.org/z/nch8fjdf5)显示了这两个问题。(1)中的最后一个元素丢失了,而 double 被解释为 int(2)。 ```txt sum(1, 5): 5 diff --git "a/C++CoreGuidelines/\347\254\2545\347\253\240-\347\261\273\345\222\214\347\261\273\345\261\202\346\254\241\347\273\223\346\236\204.md" "b/C++CoreGuidelines/\347\254\2545\347\253\240-\347\261\273\345\222\214\347\261\273\345\261\202\346\254\241\347\273\223\346\236\204.md" index 19456712..f2357947 100644 --- "a/C++CoreGuidelines/\347\254\2545\347\253\240-\347\261\273\345\222\214\347\261\273\345\261\202\346\254\241\347\273\223\346\236\204.md" +++ "b/C++CoreGuidelines/\347\254\2545\347\253\240-\347\261\273\345\222\214\347\261\273\345\261\202\346\254\241\347\273\223\346\236\204.md" @@ -60,7 +60,7 @@ Guidelines 先给出了一些概要规则,然后深入讨论了下面的特殊 概要规则相当简短,没有涉及太多细节。它们对类概括提供了有价值的深刻见解。 -> **class(类)和struct(结构体)之间的语法差异** +> **class(类)和 struct(结构体)之间的语法差异** > 本节经常提到类和结构体之间的**语义**区别。首先。**语法**上的差异是什么?差异很小,但很重要: > - 在结构体中,所有成员默认为 public(公开);类为(private)私有。 > - 继承情况也是如此。结构体默认继承权限为 public,类为 private。 @@ -240,7 +240,7 @@ Date date{ /*...*/ }; 长话短说:应用 [KISS](https://en.wikipedia.org/wiki/KISS_principle) 原则(“keep it simple,stupid”原则,保持简单,让傻瓜都能理解)。你的类型行为像普通数值一样。 -[^5]: mq白注:原文写的是“运行期开销”,英文原文是“`run-time overhead`”,不好,改掉。 +[^5]: mq 白注:原文写的是“运行期开销”,英文原文是“`run-time overhead`”,不好,改掉。 ### C.11 让具体类型规范化 @@ -438,9 +438,9 @@ double free or corruption (!prev) Program terminated with signal: SIGSEGV ``` -[^1]: mq白注:在当前版本的 [C++Core Guidelines](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c21-if-you-define-or-delete-any-copy-move-or-destructor-function-define-or-delete-them-all) 里,C.21 已经把“默认操作”改成了“拷贝、移动、析构函数”,明确剔除了默认构造函数。 +[^1]: mq 白注:在当前版本的 [C++Core Guidelines](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c21-if-you-define-or-delete-any-copy-move-or-destructor-function-define-or-delete-them-all) 里,C.21 已经把“默认操作”改成了“拷贝、移动、析构函数”,明确剔除了默认构造函数。 -[^2]: mq白注:`=delete` 就是表格中 “弃置” 的意思。 +[^2]: mq 白注:`=delete` 就是表格中 “弃置” 的意思。 ### C.22 让默认操作保持一致 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 67352c76..9f0224f5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,14 +2,14 @@ 如果你想要在题库中**添加一道题目**,请遵循如下指南: -1. 为了便于管理,题号由负责合并pr的管理人员添加,并按照pr合并时的序号递增。出题人可以不写序号,或随便写一个占位; +1. 为了便于管理,题号由负责合并 pr 的管理人员添加,并按照 pr 合并时的序号递增。出题人可以不写序号,或随便写一个占位; 2. 题目需要有一个简单的标题,说明题目的大体内容,设置为二级标题; -3. 题目需要包含日期和出题人ID,日期和时间均需要前后加反引号并加粗,日期按照 `年/月/日` 格式排列,示例:**`2023/7/21`**、**`mq白`**; -4. 题目的代码部分需要以代码块的形式给出,并注明编程语言(本仓库中一般应为c++); +3. 题目需要包含日期和出题人 ID,日期和时间均需要前后加反引号并加粗,日期按照 `年/月/日` 格式排列,示例:**`2023/7/21`**、**`mq白`**; +4. 题目的代码部分需要以代码块的形式给出,并注明编程语言(本仓库中一般应为 c++); 5. 题目中应当包含示例运行结果,同样以代码块的形式给出; 6. 难度部分可以省略,或者按照主观判断添加。题目添加后仓库管理人员可能会酌情修改; -7. github的markdown语法在换行时需要在行末添加两个空格,请在fork的仓库中确认好格式正常后再提交pr。 +7. github 的 markdown 语法在换行时需要在行末添加两个空格,请在 fork 的仓库中确认好格式正常后再提交 pr。 **太麻烦了你直接看第一题是个啥形式就可以了。** -P.S. 指南内容还在更新中,提交pr前请注意关注该指南是否有更新。 +P.S. 指南内容还在更新中,提交 pr 前请注意关注该指南是否有更新。 diff --git a/README.md b/README.md index 70db46e4..e1677b06 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
[![视频教程](https://img.shields.io/badge/%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B-bilibili-cyan)](https://www.bilibili.com/video/BV1Zj411r7eP) -[![QQ群](https://img.shields.io/badge/QQ%E7%BE%A4-%E5%8D%A2%E7%91%9F%E5%B8%9D%E5%9B%BD-blue)](https://qm.qq.com/cgi-bin/qm/qr?k=X-ouAYdQzPDQGUR7R-vECHDpXb7Uihdm&jump_from=webapi&authKey=5XYoNIfb913mo5Ff3P1nOhVy1pJgCM4Q6wAykQ+rpiDQSRu+tCXMN6yGOkjxIIrl) +[![QQ 群](https://img.shields.io/badge/QQ%E7%BE%A4-%E5%8D%A2%E7%91%9F%E5%B8%9D%E5%9B%BD-blue)](https://qm.qq.com/cgi-bin/qm/qr?k=X-ouAYdQzPDQGUR7R-vECHDpXb7Uihdm&jump_from=webapi&authKey=5XYoNIfb913mo5Ff3P1nOhVy1pJgCM4Q6wAykQ+rpiDQSRu+tCXMN6yGOkjxIIrl) [![知乎](https://img.shields.io/badge/%E7%9F%A5%E4%B9%8E-mq%E7%99%BD-yello)](https://www.zhihu.com/people/o4ze4r) [![youtube](https://img.shields.io/badge/video-YouTube-red)](https://www.youtube.com/channel/UCey35Do4RGewqr-6EiaCJrg) @@ -94,7 +94,7 @@ [提交 PR](提交pr教程.md) 不应当更改当前 `README`,请将作业提交到 `src\群友提交` 中,比如你要提交第一个作业: -你应当在 `src\群友提交\第01题` 中创建一个自己的 `.md` 或 `.cpp` 文件,**文件名以自己交流群ID命名(或 GitHub 用户名都可,方便找到本人即可)**。 +你应当在 `src\群友提交\第01题` 中创建一个自己的 `.md` 或 `.cpp` 文件,**文件名以自己交流群 ID 命名(或 GitHub 用户名都可,方便找到本人即可)**。 答题的**一般要求**如下(题目额外要求也自行注意看): @@ -224,7 +224,7 @@ requires std::regular_invocable //我们可以认为对模板形参U,F 如果没接触过约束表达式,没关系,下面将简要的介绍。 -requires 表达式如同一个返回 bool 的函数,而U和F作为类型填入 std::regular_invocable 的实参列表里,只要作为类型的U,F满足该表达式则返回true;不满足则返回 false,称为“不满足约束”。不满足约束的类型自然不会执行后续的代码。而 [std::regular_invocable](https://zh.cppreference.com/w/cpp/concepts/invocable) 我们可以简单看成对类型U的每一个值,我们是否可以调用函数F,即调用 `std::invoke` 。相当于我们在编译期对运行期做了想象,想象一下可以对U在运行期执行F吗?如果可以那满足约束。 +requires 表达式如同一个返回 bool 的函数,而 U 和 F 作为类型填入 std::regular_invocable 的实参列表里,只要作为类型的 U,F 满足该表达式则返回 true;不满足则返回 false,称为“不满足约束”。不满足约束的类型自然不会执行后续的代码。而 [std::regular_invocable](https://zh.cppreference.com/w/cpp/concepts/invocable) 我们可以简单看成对类型 U 的每一个值,我们是否可以调用函数 F,即调用 `std::invoke` 。相当于我们在编译期对运行期做了想象,想象一下可以对 U 在运行期执行 F 吗?如果可以那满足约束。 而函数主体则极为简单 @@ -237,11 +237,11 @@ std::vector& operator|(std::vector& v1, const F f) { } ``` -其中[范围表达式](https://zh.cppreference.com/w/cpp/language/range-for) `for (auto& i : v1)`,如同`for(auto i=v.begin();i=v.end();++i){f(*i)}` 我们对*vector*(范围)中的每一个元素应用一次**f**函数。返回时照常返回v1。 +其中[范围表达式](https://zh.cppreference.com/w/cpp/language/range-for) `for (auto& i : v1)`,如同`for(auto i=v.begin();i=v.end();++i){f(*i)}` 我们对*vector*(范围)中的每一个元素应用一次**f**函数。返回时照常返回 v1。 如若不使用模板,则我们的形参列表得用 [std::function](https://zh.cppreference.com/w/cpp/utility/functional/function) 来接住我们使用的函数。对范围中的每个成员应用**f**不需要返回值且需要对范围中的元素进行修改,所以第二个形参为 `std::function`,并且我们不需要对传进来的函数 **f** 进行修改与拷贝,所以加上 **const** 限定是个好习惯。 -同样的我们可以不使用范围 for 而是更简单的 `std::ranges::for_each(v1, f);` 即同上一样对范围v1内的每个元素,应用一次函数 **f**。 +同样的我们可以不使用范围 for 而是更简单的 `std::ranges::for_each(v1, f);` 即同上一样对范围 v1内的每个元素,应用一次函数 **f**。 对于使用模板的形式,我们可以使用 c++20 的简写函数模板;简而言之,在函数形参列表中 auto 占位符会为模板形参列表追加一个虚设的模板形参。最开始的模板形式可以写成 @@ -456,7 +456,7 @@ void print(std::string_view fmt,auto&&...args){ 我们只是非常简单的支持了**题目要求**的形式,给 `std::formatter` 进行特化,如果要支持比如那些 `{:6}` 之类的格式化的话,显然不行,这涉及到更多的操作。 简单的特化以及 [`std::formatter`](https://zh.cppreference.com/w/cpp/utility/format/formatter) 支持的形式可以参见[**文档**](https://zh.cppreference.com/w/cpp/utility/format/formatter)。 -一些复杂的特化,up之前也有写过,在 [**`Cookbook`**](https://github.com/Mq-b/Cpp20-STL-Cookbook-src#76%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) 中,里面有对 [`std::ranges::range`](https://zh.cppreference.com/w/cpp/ranges/range) 和 [`std::tuple`](https://zh.cppreference.com/w/cpp/utility/tuple) 的特化,支持所有形式。 +一些复杂的特化,up 之前也有写过,在 [**`Cookbook`**](https://github.com/Mq-b/Cpp20-STL-Cookbook-src#76%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) 中,里面有对 [`std::ranges::range`](https://zh.cppreference.com/w/cpp/ranges/range) 和 [`std::tuple`](https://zh.cppreference.com/w/cpp/utility/tuple) 的特化,支持所有形式。 ### 解析 @@ -477,9 +477,9 @@ std::forward(arsg2), std::forward(args3),... ``` -这样我们对每个应用到的参数用 decltype 取他的类型再作为完美转发的模板参数。这样调用 `vformat`,返回string,可以使用cout直接输出。 +这样我们对每个应用到的参数用 decltype 取他的类型再作为完美转发的模板参数。这样调用 `vformat`,返回 string,可以使用 cout 直接输出。 -而自定义类型,特化std::formatter;我们需要知道的是:想要自定义**std::formatter** 模板特化需要提供两个函数,**parse和format**,**parse** 用来处理格式说明,并且设置相关的成员变量,相对于本题我们不需要如此麻烦的写此成员函数;我们选择继承`std::formatter`的 **parse** 函数,独立实现 **format** 函数。此处模板特化的语法,不了解请复习[模板特化](https://zh.cppreference.com/w/cpp/language/template_specialization)。 +而自定义类型,特化 std::formatter;我们需要知道的是:想要自定义**std::formatter** 模板特化需要提供两个函数,**parse 和 format**,**parse** 用来处理格式说明,并且设置相关的成员变量,相对于本题我们不需要如此麻烦的写此成员函数;我们选择继承`std::formatter`的 **parse** 函数,独立实现 **format** 函数。此处模板特化的语法,不了解请复习[模板特化](https://zh.cppreference.com/w/cpp/language/template_specialization)。 ```c++ template<> @@ -573,7 +573,7 @@ class A : public Component A::component_type_id() ``` -题目要求是每一个自定义类类型(假设是X)继承 `Component`,调用 `component_type_id()` 返回的是自己独一无二的ID。其他的类型同理。 +题目要求是每一个自定义类类型(假设是 X)继承 `Component`,调用 `component_type_id()` 返回的是自己独一无二的 ID。其他的类型同理。 解决题目之前我们需要强调一个知识点: > C++ 的模板不是具体类型,实例化之后才是(即**函数模板不是函数,类模板不是类**),类模板的静态成员或静态成员函数也不属于模板,而是属于**实例化后的具体类型**,我们可以用一段代码来展示结论: @@ -593,7 +593,7 @@ int main(){ } ``` -这段代码很轻易的就展示了**静态数据成员属于模板实例化后的具体类型**。`Test::n` 和 `Test::n` 不是相同的n,并且 `Test` 和 `Test` 也不是一种类型(静态成员函数同理)。 +这段代码很轻易的就展示了**静态数据成员属于模板实例化后的具体类型**。`Test::n` 和 `Test::n` 不是相同的 n,并且 `Test` 和 `Test` 也不是一种类型(静态成员函数同理)。 所以我们的解法利用的是:不同的类型实例化 `Component` 类模板,也是不同的静态成员函数,静态成员函数里面的静态局部也都是唯一的,并且在第一次调用的时候才会初始化,后面就不会。 @@ -758,7 +758,7 @@ constexpr atomic( T desired ) noexcept; >转换构造函数也会作为用户定义的转换序列中的一部分 -`6` 会调用转换构造函数,构造出一个临时的atomic对象用来**直接初始化 `n`**,即 +`6` 会调用转换构造函数,构造出一个临时的 atomic 对象用来**直接初始化 `n`**,即 ```cpp std::atomic n(std::atomic(6)) @@ -770,11 +770,11 @@ std::atomic n(std::atomic(6)) atomic( const atomic& ) = delete; ``` -实际上atomic的复制构造被删除(同时移动构造也被抑制生成了)。所以自然而然的不允许。 +实际上 atomic 的复制构造被删除(同时移动构造也被抑制生成了)。所以自然而然的不允许。 C++17 的改动是:**复制消除变为强制要求**。 纯右值表达式作为构造对象的参数,不会再调用移动构造,也不会去检测,而是原位构造。 ->说句题外话,C++17后纯右值不可能再调用移动构造。没有移动构造或者复制构造不影响使用同类型纯右值初始化对象,如 `X x{X{}}` ,即使移动/复制构造函数**都被delete**,也无所谓,[`code`](https://godbolt.org/z/Kdncxcc3o)。 +>说句题外话,C++17后纯右值不可能再调用移动构造。没有移动构造或者复制构造不影响使用同类型纯右值初始化对象,如 `X x{X{}}` ,即使移动/复制构造函数**都被 delete**,也无所谓,[`code`](https://godbolt.org/z/Kdncxcc3o)。 --- @@ -813,8 +813,8 @@ new Exception异常.... - 难度: **★☆☆☆☆** -> 某些IDE或者平台可能会将打印的异常信息标为红色放到第一行,即 -> new Exception异常.... 这句话也可能在第一行(一般终端运行不会,默认vs也无此功能) +> 某些 IDE 或者平台可能会将打印的异常信息标为红色放到第一行,即 +> new Exception 异常.... 这句话也可能在第一行(一般终端运行不会,默认 vs 也无此功能) ### [群友提交](src/群友提交/第07题) @@ -833,7 +833,7 @@ int main() { 实际上本题是用来讽刺将 Java 的写法带入到其他语言中,也就是很经典的:**Java 人写什么都是 Java**。 只是看我们这道题,实际上你非要说 `new` 有什么不好,倒也没什么非常不行的地方,只是,没有理由自己多写一个 `delete` 表达式(或者包个智能指针)。 -> 我希望不要有人开始幻想:`throw new MyException("new Exception异常....")`因为是 `throw` 一个指针类型,所以按指针传递,效率更高。不要让我看到这种逆天想法。如果你想到这一点,那不妨思考一下,构造临时对象的开销,以及使用 `new` 表达式?说实话挺无聊的问题,只是防止有人想到这些点,以及抬杠罢了。 +> 我希望不要有人开始幻想:`throw new MyException("new Exception 异常....")`因为是 `throw` 一个指针类型,所以按指针传递,效率更高。不要让我看到这种逆天想法。如果你想到这一点,那不妨思考一下,构造临时对象的开销,以及使用 `new` 表达式?说实话挺无聊的问题,只是防止有人想到这些点,以及抬杠罢了。 --- @@ -931,7 +931,7 @@ X - 难度: **★★★☆☆** -> 本问题堪称经典,**在某著名template书籍也有提过**(虽然它完全没有讲清楚)。 +> 本问题堪称经典,**在某著名 template 书籍也有提过**(虽然它完全没有讲清楚)。 > 并且从浅薄的角度来说,本题也可以让你向其他人证明加 **`this`** 访问类成员,和不加,是有很多区别的。 提示:[**名字查找**](https://zh.cppreference.com/w/cpp/language/lookup) @@ -973,7 +973,7 @@ this->f(); 1. `this->f()` **是待决名**,所以它的查找会推迟到得知它模板实参之时(即知道父类是谁,可以访问父类)。 2. `f()` **是非待决名**,检查该模板的定义时将进行无限定的名字查找(不知道父类),按照正常的查看顺序,先类内(查找不到),然后全局(找到)。 ->补充:如果是 `msvc` 的某些早期版本,或者c++版本设置在c++20之前,会打印 `X` `X`。这是因为 `msvc`不支持 [`Two-phase name lookup`](https://learn.microsoft.com/zh-cn/archive/blogs/c/msvc%E5%B7%B2%E7%BB%8F%E6%94%AF%E6%8C%81two-phase-name-lookup)。 +>补充:如果是 `msvc` 的某些早期版本,或者 c++版本设置在 c++20之前,会打印 `X` `X`。这是因为 `msvc`不支持 [`Two-phase name lookup`](https://learn.microsoft.com/zh-cn/archive/blogs/c/msvc%E5%B7%B2%E7%BB%8F%E6%94%AF%E6%8C%81two-phase-name-lookup)。 详细的可以看看文档。实测 `Microsoft Visual Studio 17.6.4` 设置 `C++20` 之前的版本都无法得到正确结果。 --- @@ -1495,7 +1495,7 @@ int main(){ } ``` -所以说白了,就是 `T(std::forward(args)...)` 这里用的小括号进行初始化,直到C++20才允许聚合类型使用小括号初始化。 +所以说白了,就是 `T(std::forward(args)...)` 这里用的小括号进行初始化,直到 C++20才允许聚合类型使用小括号初始化。 --- @@ -1974,7 +1974,7 @@ int main() { } ``` -> 来源:[mq败](https://github.com/autobotoptimusprime)。 +> 来源:[mq 败](https://github.com/autobotoptimusprime)。 当某个命名空间的成员变量在该命名空间外被定义时,该定义中用到的名字的查找会以与在命名空间之内使用的名字相同的方式进行。 diff --git a/check/Cargo.toml b/check/Cargo.toml new file mode 100644 index 00000000..ba2ae6fe --- /dev/null +++ b/check/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "check" +version = "0.1.0" +edition = "2021" + +[dependencies] +comrak = "0.20" \ No newline at end of file diff --git a/check/src/main.rs b/check/src/main.rs new file mode 100644 index 00000000..da707cd9 --- /dev/null +++ b/check/src/main.rs @@ -0,0 +1,111 @@ +use std::{ + path::{Path, PathBuf}, + sync::atomic::{AtomicBool, Ordering}, +}; + +use comrak::{ + nodes::{AstNode, NodeValue}, + Arena, Options, +}; + +static FAILED: AtomicBool = AtomicBool::new(false); + +fn main() { + let root = Path::new(".").canonicalize().unwrap(); + let files = get_file_paths(&root); + let others = get_extension_paths(&files, &["tex", "cpp"]); + let mds = get_extension_paths(&files, &["md"]); + check_utf8(&others); + check_utf8(&mds); + check_md(&mds); + if FAILED.load(Ordering::Relaxed) { + std::process::exit(1); + } +} + +fn get_file_paths(root: &Path) -> Vec { + let mut out = Vec::::new(); + for res in std::fs::read_dir(root).unwrap() { + let path = res.unwrap().path(); + if path.is_dir() { + out.append(&mut get_file_paths(&path)); + } + if path.is_file() { + out.push(path); + } + } + out +} + +fn get_extension_paths<'a>(paths: &'a Vec, extension: &[&str]) -> Vec<&'a PathBuf> { + paths + .iter() + .filter(|path| match path.extension() { + Some(ext) => { + let ext = ext.to_str().unwrap(); + extension.contains(&ext) + } + None => false, + }) + .collect() +} + +fn check_utf8(paths: &Vec<&PathBuf>) { + for path in paths { + let data = std::fs::read(path).unwrap(); + let Err(_) = String::from_utf8(data) else { + continue; + }; + println!("({}) 无效的UTF-8", path.display()); + FAILED.store(true, Ordering::Relaxed); + } +} + +fn check_md(paths: &Vec<&PathBuf>) { + for path in paths { + let data = std::fs::read(path).unwrap(); + let Ok(str) = String::from_utf8(data) else { + continue; + }; + let arena = Arena::new(); + let options = Options::default(); + let root = comrak::parse_document(&arena, &str, &options); + iter_nodes(root, &|node| match &node.data.borrow().value { + NodeValue::Text(str) => { + let chars = str.chars().collect::>(); + for window in chars.windows(2) { + if (is_a2z(window[0]) && is_cjk(window[1])) + || (is_cjk(window[0]) && is_a2z(window[1])) + { + println!( + "({}) 两个字之间没有空格:{}{}", + path.display(), + window[0], + window[1], + ); + FAILED.store(true, Ordering::Relaxed); + } + } + } + _ => {} + }); + } +} + +fn is_a2z(ch: char) -> bool { + matches!(ch, 'A'..='Z' | 'a'..='z') +} + +fn is_cjk(ch: char) -> bool { + matches!(ch, '\u{4E00}'..='\u{9FA5}') +} + +fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) +where + F: Fn(&'a AstNode<'a>), +{ + f(node); + for c in node.children() { + iter_nodes(c, f); + } +} diff --git "a/src/\345\215\242\347\221\237\346\227\245\347\273\217/catch(auto)\347\232\204\351\227\256\351\242\230.md" "b/src/\345\215\242\347\221\237\346\227\245\347\273\217/catch(auto)\347\232\204\351\227\256\351\242\230.md" index 41f2eb40..beb71782 100644 --- "a/src/\345\215\242\347\221\237\346\227\245\347\273\217/catch(auto)\347\232\204\351\227\256\351\242\230.md" +++ "b/src/\345\215\242\347\221\237\346\227\245\347\273\217/catch(auto)\347\232\204\351\227\256\351\242\230.md" @@ -52,7 +52,7 @@ gcc 和 clang 都[无法通过编译](https://godbolt.org/z/vxTW3c4sK),实测 可以看看愚蠢的 GPT 的说法,因为这个问题就是 GPT 搞出来的。 -![GPT回答](/image/卢瑟日经/catch(auto)03.jpg) +![GPT 回答](/image/卢瑟日经/catch(auto)03.jpg) 没有什么意义,按照它那样来 diff --git "a/src/\345\215\242\347\221\237\346\227\245\347\273\217/detach\347\232\204\351\227\256\351\242\230.md" "b/src/\345\215\242\347\221\237\346\227\245\347\273\217/detach\347\232\204\351\227\256\351\242\230.md" index cbde8bdb..a96d597b 100644 --- "a/src/\345\215\242\347\221\237\346\227\245\347\273\217/detach\347\232\204\351\227\256\351\242\230.md" +++ "b/src/\345\215\242\347\221\237\346\227\245\347\273\217/detach\347\232\204\351\227\256\351\242\230.md" @@ -1,4 +1,4 @@ -# detach的问题 +# detach 的问题 ## 起因 @@ -42,7 +42,7 @@ int main() { 复制捕获就没问题了吗? -我的解释是参照了《C++CoreGuidelines解析》: +我的解释是参照了《C++CoreGuidelines 解析》: 你需要关注的是 std::cout,它是一个全局对象,它的生存期如何呢?它什么时候析构?**全局对象的生存期应当和进程绑定**。 @@ -50,7 +50,7 @@ int main() { 你认可这种说法吗?主要来自: ->*std::cout的生命周期绑定到进程的生命周期。这意味着线程可能在std::cout在屏幕上打印c++ 11之前就消失了。* +>*std::cout 的生命周期绑定到进程的生命周期。这意味着线程可能在 std::cout 在屏幕上打印 c++ 11之前就消失了。* > std::cout’s lifetime is bound to the lifetime of the process. This means that the thread thr may be gone before std::cout prints C++11 onscreen. 其实我一开始是认可的,但是后面我想到了别的问题: @@ -109,7 +109,7 @@ int main(){ 文档对 `detach` 的实现要求很少,我们上面也列举了一点。 -至于 《C++CoreGuidelines解析》 的解释(英文中文都一样),有一定道理,但并不算非常认可。毕竟当进程都结束的时候,被 `detach` 的线程可不单单是 `std::cout` 用不了,基本是啥也干不了了。 +至于 《C++CoreGuidelines 解析》 的解释(英文中文都一样),有一定道理,但并不算非常认可。毕竟当进程都结束的时候,被 `detach` 的线程可不单单是 `std::cout` 用不了,基本是啥也干不了了。 > 或许我们应该直接说:要确保 detach 的线程在主线程之前执行完毕? diff --git "a/src/\345\215\242\347\221\237\346\227\245\347\273\217/\346\225\260\347\273\204&\346\214\207\351\222\210.md" "b/src/\345\215\242\347\221\237\346\227\245\347\273\217/\346\225\260\347\273\204&\346\214\207\351\222\210.md" index 7fecde69..1fc033e8 100644 --- "a/src/\345\215\242\347\221\237\346\227\245\347\273\217/\346\225\260\347\273\204&\346\214\207\351\222\210.md" +++ "b/src/\345\215\242\347\221\237\346\227\245\347\273\217/\346\225\260\347\273\204&\346\214\207\351\222\210.md" @@ -2,7 +2,7 @@ 的确,这是非常简单也没有太大意义的问题,但是因为各种各样的原因,教学、传播、造谣、历史,等等。很多人已经无法明确这个问题了,所以我们单独讲一下。 -注意,聊C++。 +注意,聊 C++。 ## 数组与指针的关系是? @@ -15,14 +15,14 @@ > 并且甚至有人将,“数组名”,和“数组”区分,用来指代两种东西,我不清楚这是否是目前大学的考点,但我的猜测是,他们谈论起“数组名”的时候,应该要表达的是 **数组对象**,但是,**萌新能区分对象和类型?** 这太神奇了,不管。 -这种说法就如同 “**int名**” 一样愚蠢。 +这种说法就如同 “**int 名**” 一样愚蠢。 ```cpp int a = 0; // 这是一个 int 类型的对象,它的名字是 a int arr[10]{}; // 这是一个数组类型(int[10])的对象,它的名字是arr ``` -**如果你认可“数组名”这种愚蠢的说法,应该也认可“int名”、“double名”、“string名”。** +**如果你认可“数组名”这种愚蠢的说法,应该也认可“int 名”、“double 名”、“string 名”。** ## 数组和指针的区别 @@ -95,7 +95,7 @@ print(p); //Error ## 模板中数组类型 -其实上一个例子也是用了模板,不过无所谓,我们再聊点:**数组类型在C++类型系统本身的意义**。 +其实上一个例子也是用了模板,不过无所谓,我们再聊点:**数组类型在 C++类型系统本身的意义**。 数组它是一类类型,它类型本身就有意义,天生的第一无二的意义,它是数组,数组就是数组。 @@ -123,4 +123,4 @@ template < ## 总结 -聊的不算非常完全,但,也暂时够了,可以提pr修改。 +聊的不算非常完全,但,也暂时够了,可以提 pr 修改。 diff --git "a/src/\345\215\242\347\221\237\346\227\245\347\273\217/\350\265\213\345\200\274\350\277\220\347\256\227\347\254\246\346\261\202\345\200\274\351\241\272\345\272\217\351\227\256\351\242\230.md" "b/src/\345\215\242\347\221\237\346\227\245\347\273\217/\350\265\213\345\200\274\350\277\220\347\256\227\347\254\246\346\261\202\345\200\274\351\241\272\345\272\217\351\227\256\351\242\230.md" index b46e9cce..2e1023c1 100644 --- "a/src/\345\215\242\347\221\237\346\227\245\347\273\217/\350\265\213\345\200\274\350\277\220\347\256\227\347\254\246\346\261\202\345\200\274\351\241\272\345\272\217\351\227\256\351\242\230.md" +++ "b/src/\345\215\242\347\221\237\346\227\245\347\273\217/\350\265\213\345\200\274\350\277\220\347\256\227\347\254\246\346\261\202\345\200\274\351\241\272\345\272\217\351\227\256\351\242\230.md" @@ -49,7 +49,7 @@ f > 函数调用表达式中,指名函数的表达式按顺序早于每个参数表达式和每个默认实参。 -- 这应当算作 **编译器bug** +- 这应当算作 **编译器 bug** 实测 gcc msvc 都是那个结果,但是 clang 不同。 diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25404\351\242\230/Wgtteol.cpp" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25404\351\242\230/Wgtteol.cpp" index 8735f739..d450177e 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25404\351\242\230/Wgtteol.cpp" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25404\351\242\230/Wgtteol.cpp" @@ -9,10 +9,10 @@ template class Component : public ComponentBase { public: //todo... - //ʹⷽʽĵǰģ࣬ʹöX̳Component + //使用任意方式更改当前模板类,使得对于任意类型X,若其继承自Component - //X::component_type_id()õһһ޶size_t͵idڲͬXͷصֵӦͬ - //Ҫ󣺲ʹstd::type_infotypeidؼ֣id0ʼ + //则X::component_type_id()会得到一个独一无二的size_t类型的id(对于不同的X类型返回的值应不同) + //要求:不能使用std::type_info(禁用typeid关键字),所有id从0开始连续。 static size_t component_type_id() { static size_t type_id = component_type_count++; diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25406\351\242\230/Wgtteol.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25406\351\242\230/Wgtteol.md" index e1fdca00..652d17e8 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25406\351\242\230/Wgtteol.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25406\351\242\230/Wgtteol.md" @@ -1,3 +1,3 @@ -"constexpr atomic(T desired) noexcept;" һת캯 -C++17֮ǰ std::atomic < int>6һֵʽ ʼʱҪ / ƶ죬"std::atomic n = 6" д "std::atomic n (std::atomic(6))" atomicĸƹ챻ɾˡ - C++ 17֮󣬴ֵʽƶ죬ԭع졣 \ No newline at end of file +"constexpr atomic(T desired) noexcept;" 是一个转换构造函数。 +C++17之前 std::atomic < int>6是一个纯右值表达式 初始化的时候要进行 复制 / 移动构造,"std::atomic n = 6" 可以写成 "std::atomic n (std::atomic(6))" 但是 atomic 的复制构造被删除了。 +而 C++ 17之后,纯右值表达式不在移动构造,而是原地构造。 \ No newline at end of file diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25406\351\242\230/loser_linker.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25406\351\242\230/loser_linker.md" index 8027d385..d884ad73 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25406\351\242\230/loser_linker.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25406\351\242\230/loser_linker.md" @@ -1,6 +1,6 @@ -6能转换为std::atomic, +6能转换为 std::atomic, 是因为调用了`constexpr atomic(T) noexcept;`用户自定义转换构造函数, 相当于`std::atomic n = std::atomic(6);` -在c++17之前的版本,通过调用复制或移动构造函数来完成n的初始化, -标准规定std::atomic既不可复制亦不可移动,所以编译失败。 +在 c++17之前的版本,通过调用复制或移动构造函数来完成 n 的初始化, +标准规定 std::atomic 既不可复制亦不可移动,所以编译失败。 c++17标准以后,复制清除变成强制要求,与此同时,用右值初始化对象不会再调用移动构造,而是直接原位构造,所以通过编译。 \ No newline at end of file diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/baiyi-jiang.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/baiyi-jiang.md" index a4515095..9294b3ca 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/baiyi-jiang.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/baiyi-jiang.md" @@ -1,2 +1,2 @@ ## 第九题 名字查找的问题 -> 对于在模板的定义中所使用的非待决名,当检查该模板的定义时将进行无限定的名字查找.f()函数并不依赖类型T,而且根据绑定规则:非待决名在模板定义点查找并绑定。即使在模板实例化点有更好的匹配,也保持此绑定。所以t2输出“全局”。 \ No newline at end of file +> 对于在模板的定义中所使用的非待决名,当检查该模板的定义时将进行无限定的名字查找.f()函数并不依赖类型 T,而且根据绑定规则:非待决名在模板定义点查找并绑定。即使在模板实例化点有更好的匹配,也保持此绑定。所以 t2输出“全局”。 \ No newline at end of file diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/rhubarb1999.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/rhubarb1999.md" index e96d3d65..3f27008b 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/rhubarb1999.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/rhubarb1999.md" @@ -28,7 +28,7 @@ int main() { ``` > 解释 1 ->> 对于在模板的定义中所使用的非待决名,当检查该模板的定义时将进行无限定的名字查找。因为 X::f() 的作用域不在 Y 中,所以Y中的 f() 查找到 ::f() ,并这个时候绑定。 +>> 对于在模板的定义中所使用的非待决名,当检查该模板的定义时将进行无限定的名字查找。因为 X::f() 的作用域不在 Y 中,所以 Y 中的 f() 查找到 ::f() ,并这个时候绑定。 > > 解释 2 ->> 在类模板中, this 是一个待决表达式,而且显式的 this-> 可以用于强行使另一表达式变为待决的。所以 this->f() 需要推迟到知道X类型的模板实参才会进行查找。然后类的成员在对其类或其派生的类的指针类型的表达式运用 -> 运算符之后, X::f() 的作用域在 Y 中,查找到 X::f() ,所以 this->f() 绑定 X::f() 。 +>> 在类模板中, this 是一个待决表达式,而且显式的 this-> 可以用于强行使另一表达式变为待决的。所以 this->f() 需要推迟到知道 X 类型的模板实参才会进行查找。然后类的成员在对其类或其派生的类的指针类型的表达式运用 -> 运算符之后, X::f() 的作用域在 Y 中,查找到 X::f() ,所以 this->f() 绑定 X::f() 。 diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/yuzhiy.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/yuzhiy.md" index 1e7a7f7a..a1165143 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/yuzhiy.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25409\351\242\230/yuzhiy.md" @@ -1,9 +1,9 @@ void t()const { this->f(); } - *this=Y : X依赖T 待决类型实例化时查找 - y 实例化为Y,进行有限定名称查找 - 在 Y中查找f(),没找到,找基类X找到 + *this=Y : X依赖 T 待决类型实例化时查找 + y 实例化为 Y,进行有限定名称查找 + 在 Y中查找 f(),没找到,找基类 X找到 void f()const { std::cout << "X\n"; }则绑定 @@ -12,6 +12,6 @@ void t2()const { f(); } - f()不依赖T,非待决名,在模板定义点查找并绑定,进行无限定的名字查找,先检查顶层给作用域 + f()不依赖 T,非待决名,在模板定义点查找并绑定,进行无限定的名字查找,先检查顶层给作用域 void f() { std::cout << "全局\n"; } //找到,绑定并停止查找,即不再继续检查别的作用域 diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25410\351\242\230/yuzhiy.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25410\351\242\230/yuzhiy.md" index 7f6c37db..a52f8769 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25410\351\242\230/yuzhiy.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25410\351\242\230/yuzhiy.md" @@ -1,7 +1,7 @@ 第十题解答 == -也不算解答吧,终于看完那篇文章了,因为就算写出来也不算我写的就用md解释一下吧 -**思路:**先计算出结构体成员数量,再用C++17结构化绑定的语法把成员解包,分别应用函数 +也不算解答吧,终于看完那篇文章了,因为就算写出来也不算我写的就用 md 解释一下吧 +**思路:**先计算出结构体成员数量,再用 C++17结构化绑定的语法把成员解包,分别应用函数 1.先拿结构体成员数量 ```c++ struct init @@ -14,7 +14,7 @@ template constexpr auto size_(tag<4>) -> decltype(T{init{}, init{}, init{}, init{}}, 0u) { return 4u; } ``` -这里学会一个很有意思的写法:**一种常见手法,是在返回类型上使用表达式 SFINAE,其中表达式使用逗号运算符,其左子表达式是所检验 的(转型到 void 以确保不会选择返回类型上的用户定义逗号运算符),而右子表达式具有期望函数返回的类型。**[参见cpprefence](https://zh.cppreference.com/w/cpp/language/sfinae) +这里学会一个很有意思的写法:**一种常见手法,是在返回类型上使用表达式 SFINAE,其中表达式使用逗号运算符,其左子表达式是所检验 的(转型到 void 以确保不会选择返回类型上的用户定义逗号运算符),而右子表达式具有期望函数返回的类型。**[参见 cpprefence](https://zh.cppreference.com/w/cpp/language/sfinae) 类似于函数重载,不知道这样说对不对,因为结构体可以使用花括号初始化器列表初始化,则当类型可以如`T{init{}, init{}, init{}, init{}`这样展开时对应匹配,decltype(0u)返回4,不匹配时从重载集中删除。 ```c++ template @@ -38,10 +38,10 @@ struct tag : tag {}; template <> struct tag<0> {}; ``` -此处又是个技巧,因为函数重载匹配选取最佳匹配,我们考虑如下继承链 A->(派生)B->C->D 与如下函数f(A),f(B),f(C),f(D),那么`D d{};f(d)`执行调用时选取f(D),如果f(D)函数不存在那么选取f(C),依次类推具。体参考cppreference[隐式转换序列的排行](https://zh.cppreference.com/w/cpp/language/overload_resolution) -此处我们模拟一个实例`struct Y { double a{}, b{}; }y;`首先经过以下函数size_(tag<4>{}) -此处tag<4>{}这个亡值表达式正如上述继承链中的D即使他的最佳匹配从重载记中剔除也能匹配包含他的父类作为形参的函数。SFINAE实例y不匹配,那么选择size_(tag<3>)的重载版本以此类推选取到合适版本size_(tag<2>),返回结果2 -而开始的`init{}`结构体正是帮助我们进行类型转换从而进行SFINAE +此处又是个技巧,因为函数重载匹配选取最佳匹配,我们考虑如下继承链 A->(派生)B->C->D 与如下函数 f(A),f(B),f(C),f(D),那么`D d{};f(d)`执行调用时选取 f(D),如果 f(D)函数不存在那么选取 f(C),依次类推具。体参考 cppreference[隐式转换序列的排行](https://zh.cppreference.com/w/cpp/language/overload_resolution) +此处我们模拟一个实例`struct Y { double a{}, b{}; }y;`首先经过以下函数 size_(tag<4>{}) +此处 tag<4>{}这个亡值表达式正如上述继承链中的 D 即使他的最佳匹配从重载记中剔除也能匹配包含他的父类作为形参的函数。SFINAE 实例 y 不匹配,那么选择 size_(tag<3>)的重载版本以此类推选取到合适版本 size_(tag<2>),返回结果2 +而开始的`init{}`结构体正是帮助我们进行类型转换从而进行 SFINAE ```c++ template constexpr size_t size() @@ -50,7 +50,7 @@ constexpr size_t size() return size_(tag<4>{}); } ``` -2.结构化绑定解包结构体,对每个成员分别调用函数f() +2.结构化绑定解包结构体,对每个成员分别调用函数 f() 获取完结构体成员数量就可以用`if constexpr`与[c++17的结构化绑定](https://zh.cppreference.com/w/cpp/language/structured_binding)**绑定指定名称到初始化器的子对象或元素。类似引用,结构化绑定是既存对象的别名。不同于引用的是,结构化绑定的类型不必为引用类型** ```c++ template @@ -77,4 +77,4 @@ void for_each_member(T const& v, F&& f) { ``` 后记 -- -白老师你这题就是你那个知乎文章吧,我也看了你c++20的写法,主要区别还是获取成员数量的写法,感觉那个写法比这个打表适用性强,这tm结构题成员越多还要写更多重载。 \ No newline at end of file +白老师你这题就是你那个知乎文章吧,我也看了你 c++20的写法,主要区别还是获取成员数量的写法,感觉那个写法比这个打表适用性强,这 tm 结构题成员越多还要写更多重载。 \ No newline at end of file diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25411\351\242\230/loser_linker.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25411\351\242\230/loser_linker.md" index d4329f39..dfc05da8 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25411\351\242\230/loser_linker.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25411\351\242\230/loser_linker.md" @@ -18,7 +18,7 @@ template constexpr reference emplace_back(Args&&... args) ``` -emplace_back通过std::allocator_traits::construct 构造,它典型地用布置 new 于容器所提供的位置原位构造元素。 +emplace_back 通过 std::allocator_traits::construct 构造,它典型地用布置 new 于容器所提供的位置原位构造元素。 参数 args... 以 std::forward(args)... 转发到构造函数。 -c++20前,聚合体类型只能使用{}进行聚合初始化,但c++20以后,也允许使用()进行聚合初始化,所以c++20前,emplace_back找不到构造函数,c++20以后可以找到聚合初始化,所以可以通过编译, -也就是说c++20以后,`Pos p(1,2);` 等同于之前的`Pos p{ 1,2 };` \ No newline at end of file +c++20前,聚合体类型只能使用{}进行聚合初始化,但 c++20以后,也允许使用()进行聚合初始化,所以 c++20前,emplace_back 找不到构造函数,c++20以后可以找到聚合初始化,所以可以通过编译, +也就是说 c++20以后,`Pos p(1,2);` 等同于之前的`Pos p{ 1,2 };` \ No newline at end of file diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25411\351\242\230/\345\255\220\351\255\202.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25411\351\242\230/\345\255\220\351\255\202.md" index 21902521..036d2ac6 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25411\351\242\230/\345\255\220\351\255\202.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25411\351\242\230/\345\255\220\351\255\202.md" @@ -1,2 +1,2 @@ `std::vector`的`emplace_back`中使用完美转发和`placement new`进行元素的初始化(使用小括号), -这里`struct Pos`没有提供合适的构造函数,同时C++20之前聚合类型没有小括号形式的"聚合初始化",故不能编译成功。 \ No newline at end of file +这里`struct Pos`没有提供合适的构造函数,同时 C++20之前聚合类型没有小括号形式的"聚合初始化",故不能编译成功。 \ No newline at end of file diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25413\351\242\230/loser_linker.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25413\351\242\230/loser_linker.md" index e67c5a67..8990637a 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25413\351\242\230/loser_linker.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25413\351\242\230/loser_linker.md" @@ -4,7 +4,7 @@ X f(){ return std::move(x); } ``` -有问题。使用move返回局部命名对象影响nrvo优化。 +有问题。使用 move 返回局部命名对象影响 nrvo 优化。 ```c++ X&& f(){ X x; diff --git "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25413\351\242\230/\345\255\220\351\255\202.md" "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25413\351\242\230/\345\255\220\351\255\202.md" index 54ff515c..9caa20cc 100644 --- "a/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25413\351\242\230/\345\255\220\351\255\202.md" +++ "b/src/\347\276\244\345\217\213\346\217\220\344\272\244/\347\254\25413\351\242\230/\345\255\220\351\255\202.md" @@ -1,3 +1,3 @@ -1.毫无意义,甚至会影响NRVO优化:当前语境下`x`是隐式可移动实体,重载决议时会选择到移动构造。 +1.毫无意义,甚至会影响 NRVO 优化:当前语境下`x`是隐式可移动实体,重载决议时会选择到移动构造。 2.有问题:返回的是局部引用,离开函数后,产生悬垂引用。 3.没有问题:类数据成员不是隐式可移动实体,需要通过`std::move`让重载决议选择移动构造。 \ No newline at end of file diff --git "a/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231002 \345\237\272\347\241\200C++\351\242\230\347\233\256.md" "b/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231002 \345\237\272\347\241\200C++\351\242\230\347\233\256.md" index 3e644db6..b7178b16 100644 --- "a/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231002 \345\237\272\347\241\200C++\351\242\230\347\233\256.md" +++ "b/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231002 \345\237\272\347\241\200C++\351\242\230\347\233\256.md" @@ -1,4 +1,4 @@ -# 20231002 基础C++题目 +# 20231002 基础 C++题目 ## 题目 @@ -34,5 +34,5 @@ C 虽然数组类型不能直接赋值,但是作为类类型成员的数组类型,默认复制赋值函数的行为是逐元素复制。 -- 此赋值行为在C语言也是成立的。 -- 切忌将foo::a理解成指针。 +- 此赋值行为在 C 语言也是成立的。 +- 切忌将 foo::a 理解成指针。 diff --git "a/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231017 \345\237\272\347\241\200C++\351\242\230\347\233\256\357\274\210\350\275\254\346\215\242\347\232\204\344\275\277\347\224\250\357\274\211.md" "b/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231017 \345\237\272\347\241\200C++\351\242\230\347\233\256\357\274\210\350\275\254\346\215\242\347\232\204\344\275\277\347\224\250\357\274\211.md" index bfc87f5f..46c268b7 100644 --- "a/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231017 \345\237\272\347\241\200C++\351\242\230\347\233\256\357\274\210\350\275\254\346\215\242\347\232\204\344\275\277\347\224\250\357\274\211.md" +++ "b/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231017 \345\237\272\347\241\200C++\351\242\230\347\233\256\357\274\210\350\275\254\346\215\242\347\232\204\344\275\277\347\224\250\357\274\211.md" @@ -1,4 +1,4 @@ -# 20231017 基础C++题目 +# 20231017 基础 C++题目 ## 题目 diff --git "a/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231126 \345\237\272\347\241\200C++\351\242\230\347\233\256\357\274\210\346\225\260\347\273\204\351\200\200\345\214\226\357\274\211.md" "b/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231126 \345\237\272\347\241\200C++\351\242\230\347\233\256\357\274\210\346\225\260\347\273\204\351\200\200\345\214\226\357\274\211.md" index b371d176..fc435789 100644 --- "a/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231126 \345\237\272\347\241\200C++\351\242\230\347\233\256\357\274\210\346\225\260\347\273\204\351\200\200\345\214\226\357\274\211.md" +++ "b/\345\237\272\347\241\200\346\200\247C++\351\242\230\347\233\256/20231126 \345\237\272\347\241\200C++\351\242\230\347\233\256\357\274\210\346\225\260\347\273\204\351\200\200\345\214\226\357\274\211.md" @@ -1,4 +1,4 @@ -# 20231017 基础C++题目 +# 20231017 基础 C++题目 ## 题目 diff --git "a/\346\217\220\344\272\244pr\346\225\231\347\250\213.md" "b/\346\217\220\344\272\244pr\346\225\231\347\250\213.md" index 39feedbd..d7abb617 100644 --- "a/\346\217\220\344\272\244pr\346\225\231\347\250\213.md" +++ "b/\346\217\220\344\272\244pr\346\225\231\347\250\213.md" @@ -18,11 +18,11 @@ 然后会显示: -![fork后](image/pr/03.png) +![fork 后](image/pr/03.png) Repository name(仓库名) 必须写,用默认也行, Description(说明) 可写可不写,我们修改好之后直接点击绿色按钮,**Create fork** 就可以了。 -![fork后修改](image/pr/04.png) +![fork 后修改](image/pr/04.png) 这样我们就会拥有一个这个仓库的副本,默认会跳转到仓库中。 @@ -49,7 +49,7 @@ Repository name(仓库名) 必须写,用默认也行, Description(说 然后提交 pr,如下: -![提交pr](image/pr/09.png) +![提交 pr](image/pr/09.png) 确认: @@ -71,11 +71,11 @@ Repository name(仓库名) 必须写,用默认也行, Description(说 打开 vscode: -![打开vscode](image/pr/12.png) +![打开 vscode](image/pr/12.png) 打开 git 选项,点击克隆仓库: -![打开git选项克隆仓库](image/pr/13.png) +![打开 git 选项克隆仓库](image/pr/13.png) 输入我们先前复制的自己仓库的链接: