Skip to content

Commit cc294b2

Browse files
committed
revision changkun#2: 检阅部分模板相关内容
1 parent 02a1105 commit cc294b2

File tree

3 files changed

+89
-18
lines changed

3 files changed

+89
-18
lines changed

book/02-usability.md

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,8 @@ int main() {
330330
一些其他的常见用法:
331331
332332
```cpp
333-
auto i = 5; // i 被推导为 int
334-
auto arr = new auto(10) // arr 被推导为 int *
333+
auto i = 5; // i 被推导为 int
334+
auto arr = new auto(10); // arr 被推导为 int *
335335
```
336336

337337
> **注意**:`auto` 不能用于函数传参,因此下面的做法是无法通过编译的(考虑重载的问题,我们应该使用模板):
@@ -539,58 +539,74 @@ int main() {
539539
}
540540
```
541541

542-
> 直至往后的内容正在更新,尽请期待
543-
544542
## 2.5 模板
545543

544+
C++ 的模板一直是这门语言的一种特殊的艺术,模板甚至可以独立作为一门新的语言来进行使用。模板的哲学在于将一切能够在编译期处理的问题丢到编译期进行处理,仅在运行时处理那些最核心的动态服务,进而大幅优化运行期的性能。因此模板也被很多人视作 C++ 的黑魔法之一。
545+
546546
### 外部模板
547547

548-
传统 C++ 中,模板只有在使用时才会被编译器实例化。换句话说,只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要出发模板实例化
548+
传统 C++ 中,模板只有在使用时才会被编译器实例化。换句话说,只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要触发模板的实例化
549549

550-
C++11 引入了外部模板,扩充了原来的强制编译器在特定位置实例化模板的语法,使得能够显式的告诉编译器何时进行模板的实例化
550+
为此,C++11 引入了外部模板,扩充了原来的强制编译器在特定位置实例化模板的语法,使我们能够显式的通知编译器何时进行模板的实例化
551551

552552
```cpp
553-
template class std::vector<MagicClass>; // 强行实例化
554-
extern template class std::vector<MagicClass>; // 不在该编译文件中实例化模板
553+
template class std::vector<bool>; // 强行实例化
554+
extern template class std::vector<double>; // 不在该当前编译文件中实例化模板
555555
```
556556
557557
### 尖括号 ">"
558558
559559
在传统 C++ 的编译器中,`>>`一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码:
560560
561561
```cpp
562-
std::vector<std::vector<int>> mtx;
562+
std::vector<std::vector<int>> matrix;
563563
```
564564

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

567567
```cpp
568-
template<bool T> SuckType;
569-
std::vector<SuckType<(1>2)>> v; // 合法, 但不建议写出这样的代码
568+
template<bool T>
569+
class MagicType {
570+
bool magic = T;
571+
};
572+
573+
// in main function:
574+
std::vector<MagicType<(1>2)>> magic; // 合法, 但不建议写出这样的代码
570575
```
571576
572577
### 类型别名模板
573578
574-
在了解类型别名模板之前,需要理解『模板』和『类型』之间的不同。仔细体会这句话:**模板是用来产生类型的。**在传统 C++中,`typedef` 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。例如:
579+
在了解类型别名模板之前,需要理解『模板』和『类型』之间的不同。仔细体会这句话:**模板是用来产生类型的。**在传统 C++ 中,`typedef` 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。例如:
575580
576581
```cpp
577582
template<typename T, typename U>
578-
class SuckType;
583+
class MagicType {
584+
public:
585+
T dark;
586+
U magic;
587+
};
579588
580-
typedef SuckType<std::vector, std::string> NewType; // 不合法
589+
// 不合法
590+
template<typename T>
591+
typedef MagicType<std::vector<T>, std::string> FakeDarkMagic;
581592
```
582593

583594
C++11 使用 `using` 引入了下面这种形式的写法,并且同时支持对传统 `typedef` 相同的功效:
584595

585596
> 通常我们使用 `typedef` 定义别名的语法是:`typedef 原名称 新名称;`,但是对函数指针等别名的定义语法却不相同,这通常给直接阅读造成了一定程度的困难。
586597

587598
```cpp
588-
typedef int (*process)(void *); // 定义了一个返回类型为 int,参数为 void* 的函数指针类型,名字叫做 process
589-
using process = int(*)(void *); // 同上, 更加直观
590-
using NewType = SuckType<std::vector, std::string>;
599+
typedef int (*process)(void *);
600+
using NewProcess = int(*)(void *);
601+
template<typename T>
602+
using TrueDarkMagic = MagicType<std::vector<T>, std::string>;
603+
604+
int main() {
605+
TrueDarkMagic<bool> you;
606+
}
591607
```
592608
593-
<!--C++14 在这方面更进一步,提供了更加简洁的写法。命名规则为:如果标准库的某个类模板(std::template_class)只含有唯一的成员,即成员类型为`type`,那么标准库可以用 std::template_class_t<T> 作为 typename std::template_class:type 的别名。-->
609+
> 直至往后的内容正在更新,尽请期待
594610
595611
### 默认模板参数
596612

code/2/2.12.external.template.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// 2.12.external.template.cpp
3+
// chapter 2 language usability
4+
// modern cpp tutorial
5+
//
6+
// created by changkun at changkun.de
7+
//
8+
9+
#include <iostream>
10+
#include <vector>
11+
12+
template class std::vector<bool>; // forcely instantiation
13+
extern template class std::vector<double>; // external template for avoiding instantiation in this file
14+
15+
template<bool T> class MagicType {
16+
bool magic = T;
17+
};
18+
19+
int main() {
20+
// the >> in template
21+
std::vector<std::vector<int>> matrix;
22+
std::vector<MagicType<(1>2)>> magic;
23+
}

code/2/2.13.alias.template.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// 2.13.alias.template.cpp
3+
// chapter 2 language usability
4+
// modern cpp tutorial
5+
//
6+
// created by changkun at changkun.de
7+
//
8+
9+
#include <iostream>
10+
#include <vector>
11+
#include <string>
12+
13+
template<typename T, typename U>
14+
class MagicType {
15+
public:
16+
T dark;
17+
U magic;
18+
};
19+
20+
// illegal
21+
// template<typename T>
22+
// typedef MagicType<std::vector<T>, std::string> FakeDarkMagic;
23+
24+
typedef int (*process)(void *);
25+
using NewProcess = int(*)(void *);
26+
template<typename T>
27+
using TrueDarkMagic = MagicType<std::vector<T>, std::string>;
28+
29+
int main() {
30+
// FakeDarkMagic<bool> me;
31+
TrueDarkMagic<bool> you;
32+
}

0 commit comments

Comments
 (0)