Skip to content

Commit dd8cf90

Browse files
committed
see changkun#2: move non-type template param auto to ch02
1 parent b073e29 commit dd8cf90

10 files changed

+145
-176
lines changed

assets/donate.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ order: 1
88

99
I would love if you support me to make the book better:
1010

11-
[![](https://img.shields.io/badge/donate-PayPal-104098.svg?style=popout-square&logo=PayPal)](https://www.paypal.me/ouchangkun/4.99eur)
11+
[![](https://img.shields.io/badge/donate-PayPal-104098.svg?style=popout-square&logo=PayPal)](https://www.paypal.me/changkunde/4.99eur)
1212

1313
## 资助
1414

book/zh-cn/02-usability.md

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ class Magic<int,
651651
std::vector<int>>> darkMagic;
652652
```
653653
654-
既然是任意形式,所以个数为0的模板参数也是可以的:`class Magic<> nothing;`。
654+
既然是任意形式,所以个数为 0 的模板参数也是可以的:`class Magic<> nothing;`。
655655
656656
如果不希望产生的模板参数个数为0,可以手动的定义至少一个模板参数:
657657
@@ -670,8 +670,8 @@ template<typename... Args> void printf(const std::string &str, Args... args);
670670
首先,我们可以使用 `sizeof...` 来计算参数的个数,:
671671
672672
```cpp
673-
template<typename... Args>
674-
void magic(Args... args) {
673+
template<typename... Ts>
674+
void magic(Ts... args) {
675675
std::cout << sizeof...(args) << std::endl;
676676
}
677677
```
@@ -692,20 +692,22 @@ magic(1, ""); // 输出2
692692
```cpp
693693
#include <iostream>
694694
template<typename T0>
695-
void printf(T0 value) {
695+
void printf1(T0 value) {
696696
std::cout << value << std::endl;
697697
}
698-
template<typename T, typename... Args>
699-
void printf(T value, Args... args) {
698+
template<typename T, typename... Ts>
699+
void printf1(T value, Ts... args) {
700700
std::cout << value << std::endl;
701-
printf(args...);
701+
printf1(args...);
702702
}
703703
int main() {
704-
printf(1, 2, "123", 1.1);
705-
return 0;
704+
printf1(1, 2, "123", 1.1);
705+
return 0;
706706
}
707707
```
708708

709+
**2. 变参模板展开**
710+
709711
你应该感受到了这很繁琐,在 C++17 中增加了变参模板展开的支持,于是你可以在一个函数中完成 `printf` 的编写:
710712

711713
```cpp
@@ -716,51 +718,65 @@ void printf(T0 t0, T... t) {
716718
}
717719
```
718720
721+
> 事实上,有时候我们虽然使用了变参模板,却不一定需要对参数做逐个遍历,我们可以利用 `std::bind` 及完美转发等特性实现对函数和参数的绑定,从而达到成功调用的目的。
719722
723+
### 折叠表达式
720724
721-
**2. 初始化列表展开**
722-
723-
> 这个方法需要之后介绍的知识,读者可以简单阅读一下,将这个代码段保存,在后面的内容了解过了之后再回过头来阅读此处方法会大有收获。
724-
725-
递归模板函数是一种标准的做法,但缺点显而易见的在于必须定义一个终止递归的函数。
726-
727-
这里介绍一种使用初始化列表展开的黑魔法:
725+
C++ 17 中将变长参数这种特性进一步带给了表达式,考虑下面这个例子:
728726
729727
```cpp
730-
// 编译这个代码需要开启 -std=c++14
731-
template<typename T, typename... Args>
732-
auto print(T value, Args... args) {
733-
std::cout << value << std::endl;
734-
return std::initializer_list<T>{([&] {
735-
std::cout << args << std::endl;
736-
}(), value)...};
728+
#include <iostream>
729+
template<typename ... T>
730+
auto sum(T ... t) {
731+
return (t + ...);
737732
}
738733
int main() {
739-
print(1, 2.1, "123");
740-
return 0;
734+
std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
741735
}
742736
```
743737

744-
在这个代码中,额外使用了 C++11 中提供的初始化列表以及 Lambda 表达式的特性(下一节中将提到),而 std::initializer_list 也是 C++11 新引入的容器(以后会介绍到)。
738+
### 非类型模板参数推导
745739

746-
通过初始化列表,`(lambda 表达式, value)...` 将会被展开。由于逗号表达式的出现,首先会执行前面的 lambda 表达式,完成参数的输出。唯一不美观的地方在于如果不使用 `return` 编译器会给出未使用的变量作为警告
740+
前面我们主要提及的是模板参数的一种形式:类型模板参数
747741

748-
> 事实上,有时候我们虽然使用了变参模板,却不一定需要对参数做逐个遍历,我们可以利用 `std::bind` 及完美转发等特性实现对函数和参数的绑定,从而达到成功调用的目的。
742+
```cpp
743+
template <typename T, typename U>
744+
auto add(T t, U u) {
745+
return t+u;
746+
}
747+
```
749748
750-
> 关于这方面的使用技巧,请参考习题,TODO
749+
其中模板的参数 `T` 和 `U` 为具体的类型。
750+
但还有一种常见模板参数形式可以让不同字面量成为模板参数,即非类型模板参数:
751751
752-
### 折叠表达式
752+
```cpp
753+
template <typename T, int BufSize>
754+
class buffer_t {
755+
public:
756+
T& alloc();
757+
void free(T& item);
758+
private:
759+
T data[BufSize];
760+
}
753761
754-
C++ 17 中将变长参数这种特性进一步带给了表达式,考虑下面这个例子:
762+
buffer_t<int, 100> buf; // 100 作为模板参数
763+
```
764+
765+
在这种模板参数形式下,我们可以将 `100` 作为模板的参数进行传递。
766+
在 C++11 引入了类型推导这一特性后,我们会很自然的问,既然此处的模板参数
767+
以具体的字面量进行传递,能否让编译器辅助我们进行类型推导,
768+
通过使用占位符 `auto` 从而不再需要明确指明类型?
769+
幸运的是,C++17 引入了这一特性,我们的确可以 `auto` 关键字,让编译器辅助完成具体类型的推导,
770+
例如:
755771

756772
```cpp
757-
#include <iostream>
758-
template<typename ... T>
759-
auto sum(T ... t) {
760-
return (t + ...);
773+
template <auto value> void foo() {
774+
std::cout << value << std::endl;
775+
return;
761776
}
777+
762778
int main() {
763-
std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
779+
foo<10>(); // value 被推导为 int 类型
764780
}
765781
```
766782

book/zh-cn/10-cpp20.md

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,49 +12,9 @@ order: 10
1212

1313
本章对即将到来的 C++17 进行介绍,几个月前(2016 年),目前为止,还没有一个正式发布的编译器来编译 C++17 特性的代码,本节作为扩展主题,供对 C++ 的历史进程及其未来发展感兴趣的读者阅读。
1414

15-
## 10.1 主要入选特性
16-
17-
### 非类型模板参数的 auto
18-
19-
模板参数分为两种,一种是类型模板参数,也是我们用得最多的一种:
20-
21-
```cpp
22-
template <typename T, typename U>
23-
auto add(T t, U u) {
24-
return t+u;
25-
}
26-
```
27-
28-
里面的 `T` 和 `U` 都是类型模板参数。另一种是非类型模板参数,它可以让不同的字面量成为模板的参数:
29-
30-
```cpp
31-
template <typename T, int BufSize>
32-
class buffer_t {
33-
public:
34-
T& alloc();
35-
void free(T& item);
36-
private:
37-
T data[BufSize];
38-
}
39-
40-
buffer_t<int, 100> buf; // 100 作为模板参数
41-
```
42-
43-
遗憾的是我们在编写模板的时候就必须明确非类型模板参数的具体类型,C++17 打破了这一限制,让我们能够在非类型模板参数中使用 `auto` 关键字,从而让编译器推导具体的类型:
44-
45-
```cpp
46-
template <auto value> void foo() {
47-
return;
48-
}
49-
50-
foo<10>(); // value 被推导为 int 类型
51-
```
52-
53-
## 10.2 未入选特性
54-
5515
C++ 组委会在讨论投票最终确定 C++17 有很多提案,诸如 **Concepts**/**Ranges**/**Module** 等等,其中最受关注的就是 **Concepts**,可惜这一提案最终被拒,作为技术规范(Technical Specifications, TS) 将其发布。
5616

57-
### Concepts TS
17+
## Concepts TS
5818

5919
**Concepts** 是对 C++ 模板编程的进一步增强扩展。简单来说,**Concepts** 是一种编译期的特性,它能够让编译器在编译期时对模板参数进行判断,从而大幅度增强我们在 C++ 中模板编程的体验。使用模板进行编程时候我们经常会遇到各种令人发指的错误,这是因为到目前为止我们始终不能够对模板参数进行检查与限制,例如下面简单的两行代码会造成大量的几乎不可读的编译错误:
6020

code/2/2.12.external.template.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ template<bool T> class MagicType {
1919
int main() {
2020
// the >> in template
2121
std::vector<std::vector<int>> matrix;
22-
std::vector<MagicType<(1>2)>> magic;
22+
std::vector<MagicType<(1>2)>> magic; // legal, but not recommended
2323
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// 2.4.default.template.param.cpp
3+
// chapter 2 language usability
4+
// modern cpp tutorial
5+
//
6+
// created by changkun at changkun.de
7+
//
8+
9+
#include <iostream>
10+
11+
template<typename T = int, typename U = int>
12+
auto add(T x, U y) -> decltype(x+y) {
13+
return x+y;
14+
}
15+
16+
int main() {
17+
std::cout << add(1, 2) << std::endl;
18+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// 2.15.variadic.template.param.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+
// sizeof...
14+
template<typename... Ts>
15+
void magic(Ts... args) {
16+
std::cout << sizeof...(args) << std::endl;
17+
}
18+
19+
20+
// 1 recursive parameter unpack
21+
template<typename T0>
22+
void printf1(T0 value) {
23+
std::cout << value << std::endl;
24+
}
25+
template<typename T, typename... Ts>
26+
void printf1(T value, Ts... args) {
27+
std::cout << value << std::endl;
28+
printf1(args...);
29+
}
30+
31+
// 2 variadic template parameter unfold
32+
template<typename T0, typename... T>
33+
void printf2(T0 t0, T... t) {
34+
std::cout << t0 << std::endl;
35+
if constexpr (sizeof...(t) > 0) printf2(t...);
36+
}
37+
38+
int main() {
39+
magic();
40+
magic(1);
41+
magic(1,"");
42+
43+
printf1(1, 2, "123", 1.1);
44+
printf2(1, 2.3, "abc");
45+
return 0;
46+
}

code/2/todo/fold.expression.cpp renamed to code/2/2.16.fold.expression.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
//
2+
// 2.16.fold.expression.cpp
3+
// chapter 2 language usability
4+
// modern cpp tutorial
5+
//
6+
// created by changkun at changkun.de
7+
//
8+
19
#include <iostream>
210
template<typename ... T>
311
auto sum(T ... t) {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// 2.18.non.type.template.auto.cpp
3+
// chapter 2 language usability
4+
// modern cpp tutorial
5+
//
6+
// created by changkun at changkun.de
7+
//
8+
9+
#include <iostream>
10+
11+
template <auto value> void foo() {
12+
std::cout << value << std::endl;
13+
return;
14+
}
15+
16+
int main() {
17+
foo<10>(); // value is deduced as type int
18+
}

code/2/todo/2.6.cpp

Lines changed: 0 additions & 76 deletions
This file was deleted.

0 commit comments

Comments
 (0)