@@ -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>
694694template<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}
703703int 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}
738733int 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+
762778int 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
0 commit comments