Skip to content

Commit 45618ad

Browse files
authored
book: fmt (changkun#154)
* Add space between english and chinease char * Add space between english and chinease char * book: typo fixes Co-authored-by: guoliang wang <tswanggl@hotmail.com>
1 parent 938dcd9 commit 45618ad

File tree

3 files changed

+33
-26
lines changed

3 files changed

+33
-26
lines changed

book/zh-cn/00-preface.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ C++17 则是近三年依赖 C++ 社区一致推进的方向,也指出了**现
2323

2424
## 目标读者
2525

26-
1. 本书假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++进行编码的人、渴望在短时间内迅速了解**现代 C++** 特性的人非常适合阅读本书;
26+
1. 本书假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++ 进行编码的人、渴望在短时间内迅速了解**现代 C++** 特性的人非常适合阅读本书;
2727
2. 本书一定程度上介绍了一些现代 C++ 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习现代 C++ 的读者,本书的定位系**现代 C++ 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 **现代 C++** 的熟悉度。
2828

2929
## 本书目的

book/zh-cn/01-intro.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ int main() {
9191
gcc -c foo.c
9292
```
9393

94-
编译出 `foo.o` 文件,再使用 `clang++` 将 C++代码和 `.o` 文件链接起来(或者都编译为 `.o` 再统一链接):
94+
编译出 `foo.o` 文件,再使用 `clang++` 将 C++ 代码和 `.o` 文件链接起来(或者都编译为 `.o` 再统一链接):
9595

9696
```bash
9797
clang++ 1.1.cpp foo.o -std=c++2a -o 1.1
@@ -120,7 +120,7 @@ clean:
120120

121121
> 注意:`Makefile` 中的缩进是制表符而不是空格符,如果你直接复制这段代码到你的编辑器中,制表符可能会被自动替换掉,请自行确保在 `Makefile` 中的缩进是由制表符完成的。
122122
>
123-
> 如果你还不知道 Makefile 的使用也没有关系,本教程中不会构建过于复杂的代码,简单的在命令行中使用 `clang++ -std=c++2a` 也可以阅读本书。
123+
> 如果你还不知道 `Makefile` 的使用也没有关系,本教程中不会构建过于复杂的代码,简单的在命令行中使用 `clang++ -std=c++2a` 也可以阅读本书。
124124

125125
如果你是首次接触现代 C++,那么你很可能还看不懂上面的那一小段代码,即:
126126

book/zh-cn/02-usability.md

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ order: 2
1414

1515
### nullptr
1616

17-
`nullptr` 出现的目的是为了替代 `NULL`。在某种意义上来说,传统 C++ 会把 `NULL``0` 视为同一种东西,这取决于编译器如何定义 NULL,有些编译器会将 NULL 定义为 `((void*)0)`,有些则会直接将其定义为 `0`
17+
`nullptr` 出现的目的是为了替代 `NULL`。在某种意义上来说,传统 C++ 会把 `NULL``0` 视为同一种东西,这取决于编译器如何定义 `NULL`,有些编译器会将 `NULL` 定义为 `((void*)0)`,有些则会直接将其定义为 `0`
1818

1919
C++ **不允许**直接将 `void *` 隐式转换到其他类型。但如果编译器尝试把 `NULL` 定义为 `((void*)0)`,那么在下面这句代码中:
2020

2121
```cpp
2222
char *ch = NULL;
2323
```
2424

25-
没有了 `void *` 隐式转换的 C++ 只好将`NULL` 定义为 `0`。而这依然会产生新的问题,将 `NULL` 定义成 0 将导致 `C++` 中重载特性发生混乱。考虑下面这两个 `foo` 函数:
25+
没有了 `void *` 隐式转换的 C++ 只好将 `NULL` 定义为 `0`。而这依然会产生新的问题,将 `NULL` 定义成 `0` 将导致 `C++` 中重载特性发生混乱。考虑下面这两个 `foo` 函数:
2626

2727
```cpp
2828
void foo(char*);
@@ -31,9 +31,9 @@ void foo(int);
3131
3232
那么 `foo(NULL);` 这个语句将会去调用 `foo(int)`,从而导致代码违反直觉。
3333
34-
为了解决这个问题,C++11 引入了 `nullptr` 关键字,专门用来区分空指针、0。而 `nullptr` 的类型为 `nullptr_t`,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。
34+
为了解决这个问题,C++11 引入了 `nullptr` 关键字,专门用来区分空指针、`0`。而 `nullptr` 的类型为 `nullptr_t`,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。
3535
36-
你可以尝试使用 clang++ 编译下面的代码:
36+
你可以尝试使用 `clang++` 编译下面的代码:
3737
3838
```cpp
3939
#include <iostream>
@@ -73,11 +73,11 @@ foo(char*) is called
7373

7474
从输出中我们可以看出,`NULL` 不同于 `0``nullptr`。所以,请养成直接使用 `nullptr`的习惯。
7575

76-
此外,在上面的代码中,我们使用了 `decltype``std::is_same` 这两个属于现代 C++ 的语法,简单来说,`decltype` 用于类型推导,而 `std::is_same` 用于比较两个类型是否相等,我们会在后面 [decltype](#decltype) 一节中详细讨论。
76+
此外,在上面的代码中,我们使用了 `decltype``std::is_same` 这两个属于现代 C++ 的语法,简单来说,`decltype` 用于类型推导,而 `std::is_same` 用于比较两个类型是否相同,我们会在后面 [decltype](#decltype) 一节中详细讨论。
7777

7878
### constexpr
7979

80-
C++ 本身已经具备了常量表达式的概念,比如 1+2, 3*4 这种表达式总是会产生相同的结果并且没有任何副作用。如果编译器能够在编译时就把这些表达式直接优化并植入到程序运行时,将能增加程序的性能。一个非常明显的例子就是在数组的定义阶段:
80+
C++ 本身已经具备了常量表达式的概念,比如 `1+2`, `3*4` 这种表达式总是会产生相同的结果并且没有任何副作用。如果编译器能够在编译时就把这些表达式直接优化并植入到程序运行时,将能增加程序的性能。一个非常明显的例子就是在数组的定义阶段:
8181

8282
```cpp
8383
#include <iostream>
@@ -123,15 +123,15 @@ int main() {
123123
124124
C++11 提供了 `constexpr` 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式,这个关键字明确的告诉编译器应该去验证 `len_foo` 在编译期就应该是一个常量表达式。
125125
126-
此外,`constexpr` 的函数可以使用递归
126+
此外,`constexpr` 修饰的函数可以使用递归
127127
128128
```cpp
129129
constexpr int fibonacci(const int n) {
130130
return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
131131
}
132132
```
133133

134-
从 C++14 开始,constexpr 函数可以在内部使用局部变量、循环和分支等简单语句,例如下面的代码在 C++11 的标准下是不能够通过编译的:
134+
从 C++14 开始,`constexpr` 函数可以在内部使用局部变量、循环和分支等简单语句,例如下面的代码在 C++11 的标准下是不能够通过编译的:
135135

136136
```cpp
137137
constexpr int fibonacci(const int n) {
@@ -181,7 +181,7 @@ int main() {
181181
}
182182
```
183183

184-
在上面的代码中,我们可以看到 `itr` 这一变量是定义在整个 `main()` 的作用域内的,这导致当我们需要再次遍历整个 `std::vectors` 时,需要重新命名另一个变量。C++17 消除了这一限制,使得我们可以在 if(或 switch)中完成这一操作:
184+
在上面的代码中,我们可以看到 `itr` 这一变量是定义在整个 `main()` 的作用域内的,这导致当我们需要再次遍历整个 `std::vectors` 时,需要重新命名另一个变量。C++17 消除了这一限制,使得我们可以在 `if`(或 `switch`)中完成这一操作:
185185

186186
```cpp
187187
// 将临时变量放到 if 语句内
@@ -295,7 +295,7 @@ int main() {
295295

296296
## 2.3 类型推导
297297

298-
在传统 C 和 C++中,参数的类型都必须明确定义,这其实对我们快速进行编码没有任何帮助,尤其是当我们面对一大堆复杂的模板类型时,必须明确的指出变量的类型才能进行后续的编码,这不仅拖慢我们的开发效率,也让代码变得又臭又长。
298+
在传统 C 和 C++ 中,参数的类型都必须明确定义,这其实对我们快速进行编码没有任何帮助,尤其是当我们面对一大堆复杂的模板类型时,必须明确的指出变量的类型才能进行后续的编码,这不仅拖慢我们的开发效率,也让代码变得又臭又长。
299299

300300
C++11 引入了 `auto``decltype` 这两个关键字实现了类型推导,让编译器来操心变量的类型。这使得 C++ 也具有了和其他现代编程语言一样,某种意义上提供了无需操心变量类型的使用习惯。
301301

@@ -369,7 +369,7 @@ auto arr = new auto(10); // arr 被推导为 int *
369369
370370
### decltype
371371
372-
`decltype` 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 `typeof` 很相似:
372+
`decltype` 关键字是为了解决 `auto` 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 `typeof` 很相似:
373373
374374
```cpp
375375
decltype(表达式)
@@ -403,7 +403,7 @@ type z == type x
403403

404404
### 尾返回类型推导
405405

406-
你可能会思考,在介绍 `auto`时,我们已经提过 `auto` 不能用于函数形参进行类型推导,那么 `auto` 能不能用于推导函数的返回类型呢?还是考虑一个加法函数的例子,在传统 C++ 中我们必须这么写:
406+
你可能会思考,在介绍 `auto` 时,我们已经提过 `auto` 不能用于函数形参进行类型推导,那么 `auto` 能不能用于推导函数的返回类型呢?还是考虑一个加法函数的例子,在传统 C++ 中我们必须这么写:
407407

408408
```cpp
409409
template<typename R, typename T, typename U>
@@ -423,7 +423,7 @@ R add(T x, U y) {
423423
decltype(x+y) add(T x, U y)
424424
```
425425

426-
但事实上这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,`x``y` 尚未被定义。为了解决这个问题,C++11 还引入了一个叫做尾返回类型(trailing return type),利用 auto 关键字将返回类型后置:
426+
但事实上这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,`x``y` 尚未被定义。为了解决这个问题,C++11 还引入了一个叫做尾返回类型(trailing return type),利用 `auto` 关键字将返回类型后置:
427427

428428
```cpp
429429
template<typename T, typename U>
@@ -575,7 +575,7 @@ extern template class std::vector<double>; // 不在该当前编译文件中实
575575
std::vector<std::vector<int>> matrix;
576576
```
577577

578-
这在传统C++编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于像下面这种写法都能够通过编译:
578+
这在传统 C++ 编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于像下面这种写法都能够通过编译:
579579

580580
```cpp
581581
template<bool T>
@@ -624,13 +624,17 @@ int main() {
624624
我们可能定义了一个加法函数:
625625
626626
```cpp
627+
// c++11 version
627628
template<typename T, typename U>
628629
auto add(T x, U y) -> decltype(x+y) {
629630
return x+y;
630631
}
632+
633+
// Call add function
634+
auto ret = add<int, int>(1,3);
631635
```
632636

633-
但在使用时发现,要使用 add,就必须每次都指定其模板参数的类型。
637+
但在使用时发现,要使用 `add`,就必须每次都指定其模板参数的类型。
634638

635639
在 C++11 中提供了一种便利,可以指定模板的默认参数:
636640

@@ -639,6 +643,9 @@ template<typename T = int, typename U = int>
639643
auto add(T x, U y) -> decltype(x+y) {
640644
return x+y;
641645
}
646+
647+
// Call add function
648+
auto ret = add(1,3);
642649
```
643650
644651
### 变长参数模板
@@ -661,9 +668,9 @@ class Magic<int,
661668
std::vector<int>>> darkMagic;
662669
```
663670
664-
既然是任意形式,所以个数为 0 的模板参数也是可以的:`class Magic<> nothing;`。
671+
既然是任意形式,所以个数为 `0` 的模板参数也是可以的:`class Magic<> nothing;`。
665672
666-
如果不希望产生的模板参数个数为0,可以手动的定义至少一个模板参数:
673+
如果不希望产生的模板参数个数为 `0`,可以手动的定义至少一个模板参数:
667674
668675
```cpp
669676
template<typename Require, typename... Args> class Magic;
@@ -672,7 +679,7 @@ template<typename Require, typename... Args> class Magic;
672679
变长参数模板也能被直接调整到到模板函数上。传统 C 中的 `printf` 函数,
673680
虽然也能达成不定个数的形参的调用,但其并非类别安全。
674681
而 C++11 除了能定义类别安全的变长参数函数外,
675-
还可以使类似 printf 的函数能自然地处理非自带类别的对象。
682+
还可以使类似 `printf` 的函数能自然地处理非自带类别的对象。
676683
除了在模板参数中能使用 `...` 表示不定长模板参数外,
677684
函数参数也使用同样的表示法代表不定长参数,
678685
这也就为我们简单编写变长参数函数提供了便捷的手段,例如:
@@ -847,7 +854,7 @@ int main() {
847854
848855
### 继承构造
849856
850-
在传统 C++ 中,构造函数如果需要继承是需要将参数一一传递的,这将导致效率低下。C++11 利用关键字 using 引入了继承构造函数的概念:
857+
在传统 C++ 中,构造函数如果需要继承是需要将参数一一传递的,这将导致效率低下。C++11 利用关键字 `using` 引入了继承构造函数的概念:
851858
852859
```cpp
853860
#include <iostream>
@@ -875,7 +882,7 @@ int main() {
875882

876883
### 显式虚函数重载
877884

878-
在传统 C++中,经常容易发生意外重载虚函数的事情。例如:
885+
在传统 C++ 中,经常容易发生意外重载虚函数的事情。例如:
879886

880887
```cpp
881888
struct Base {
@@ -975,7 +982,7 @@ if (new_enum::value3 == new_enum::value4) {
975982
}
976983
```
977984

978-
在这个语法中,枚举类型后面使用了冒号及类型关键字来指定枚举中枚举值的类型,这使得我们能够为枚举赋值(未指定时将默认使用 int)。
985+
在这个语法中,枚举类型后面使用了冒号及类型关键字来指定枚举中枚举值的类型,这使得我们能够为枚举赋值(未指定时将默认使用 `int`)。
979986

980987
而我们希望获得枚举值的值时,将必须显式的进行类型转换,不过我们可以通过重载 `<<` 这个算符来进行输出,可以收藏下面这个代码段:
981988

@@ -998,8 +1005,8 @@ std::cout << new_enum::value3 << std::endl
9981005

9991006
本节介绍了现代 C++ 中对语言可用性的增强,其中笔者认为最为重要的几个特性是几乎所有人都需要了解并熟练使用的:
10001007

1001-
1. auto 类型推导
1002-
2. 范围 for 迭代
1008+
1. `auto` 类型推导
1009+
2. 范围 `for` 迭代
10031010
3. 初始化列表
10041011
4. 变参模板
10051012

0 commit comments

Comments
 (0)