1. 可变模板参数
 这在面向对象的课程就讲过了。 值得注意的是,2与3是可以并存的。在VS2019中测试如下:  可以看到都是调用第一个,说明
template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
更加特化,而另一个更泛化,优先调用更特化版本的。 下面两张图为标准库的一些片段,可以看到一些调用流程,学习一下:  
2. Space in Template Expressions
其实就是支持现在常见的写法了,之前一定要加空格。 
3. nullptr and std::nullptr_t, auto
新增nullptr,见下图:    
4. Uniform Initialization
一致性初始化:现在新特性下可以统一用 {} 来初始化  上图有深入的实现的理解。
5. Initializer Lists
如第四点那张图,编译器看到大括号 {t1, t2, …, tn} 就把他们看作一包(initializer_list)   上面那样设初值需要initializer_list类来实现,这在标准库的实现里有很大的影响。   如上图,P s={77, 5}; 也是调用构造函数。 一包(initializer_list) 可以接受任意个数。 如果没有版本2,那么在P q{77, 5}; 就会调用1,这时候编译器会把参数拆开,一个一个传给构造函数(而在之前则是直接看作一包!)。同样s也可以,但是r就编译不过去(有3个参数,但构造函数只有两个)。 而他的内部是array实现的:   回过来看:   如上可见其在标准库所占的成分,影响很大。  并且通过这样的方式,我们可以实现min和max函数一次传一包参数。
6. explicit for ctors taking more than one argument
2.0版本之前:  explicit主要用在构造函数之前。这在之前的面向对象下有讲过。 2.0之前只有单一实参可以。 2.0之后: 
7. range-based for statement
 编译器解释如下:  如下图加了explicit就不能这样用(因为赋值时要做转换,但explicit不允许): 
8. =default, =delete
这两个关键词主要用在构造函数、拷贝构造、拷贝赋值等函数上,这些特殊的函数也叫Big-Three,而当2.0之后多了右值引用,也叫Big-Five,如下图:   C++2.0之前的复习:  即空的class编译器会自动声明这些默认版本的函数。且都是inline的。  只要这个类带有pointer-member,基本就要写Big-Three,而不带则一般不需要写。 比如上图复数类的实现没有指针成员,则没有Big-Three,而string有:  再往下看:  =delete告诉编译器不要定义他。 而像NoCopy这样子太绝对了。现实中我们还可以开一个后门,把拷贝构造、拷贝赋值写在private里:  这样就只有friends和members可以去copy。
9. Alias Template
化名:  如上图,可以把vector指定自己的分配器,而用别名就避免了每一次使用都要写那么长。 而使用宏则无法达到相同效果。 但是使用化名无法对化名做特化。(化名无法代表本尊)  本意想写个函数测试容器可搬不可搬(move),来测试他们的时间(这里没写出来),但是放进来的参数一定是object,怎么能拿他的type去做文章呢?简直是天方夜谭。 后来改动如上图右边,一个typename后面加()表示临时的变量,想拿这样的对象拿来用,但是Container怎么确定是一个模板呢,仍然是天方夜谭。 再次改动仍然不认识:  最后改动:  想要得到容器内部的类型需要萃取机traits。可以办到,但还是难以实现原本的接口。 但要是还是不死心要像之前一样弄出接口如(list, MyString)呢?下面引出模板模板参数以及化名的用法:
10. Template template parameter
 上图事实上有点不好理解(class那里省略了T),可以看一下之前面向对象下的课件:  然而这样使用仍然会报错。 观察标准库源码,知道vector模板第二个参数有默认值,而在模板模板参数无法推出来。 解决方法: 
11. Type Alias, noexcept, override, final
其实Type Alias类型别名就类似typedef:  using的用法:  在某一些class里头写如using _Base::_M_allocate;那么之后就可以只写_M_allocate而不用加原来的类::
在某条件下一定不会发出异常:  这里void foo () noexcept; 的后面的条件默认为true,即一定不会发出异常。 异常如果抛出没有被处理,就会一直往源头去抛,比如A调用B,B抛出异常,但A没处理,就会继续往调用A的地方去传递。要是一直没有处理异常程序就会崩溃。  如上,通过通知C++noexcept告诉上层vector可以用搬移的函数。(只有vector会成长grows)避免copy,效率提升。  override需要函数签名完全相同。这个关键字帮助编译器检查。(把要重载的心意告诉编译器)  final:第一种用法: 修饰class,告诉别人不能被继承 修饰函数function,告诉别人(比如继承过来)不能复写
12. decltype
得到对象的type:  可以把这个关键字想象成typeof:  有三种用法,我们分别来看一下:
- 让编译器通过。
在模板中不知道真正的x + y是什么类型,比如苹果类 + 梨子类,可能得到的是水果类(向上转型了),但是我们不知道究竟是什么。 但是简单的像上面这样写是通不过的,我们要使用auto关键字,结合decltype指定方式。有点点像lambda表达式。  - 适用于元编程(metaprogramming,不要想的太严肃了,先想成就是在函数模板里用)
 只要用到了T::如上图的 T::iterator,就要加typename,以显示地告诉编译器T::iterator是一个类型名。 而由于模板只是一个半成品,用的时候如图传入一个复数的临时对象,但是复数类不是容器没有iterator,就会报错。 - lambda
 lambda的type是很难写出来的,一般都是直接auto,decltype传入容器当作typeof。
13. lambdas
下图中间的那一段的小括号就不是产生临时对象了,而是执行,但是一般我们会按下面那一段去做:  mutable(中括号捕获的值可否改变), throwSpec(一个函数可不可以抛出异常), retType(返回类型) 三者有一个就要写():  注意看下图左侧的结果,传入的id不是按引用传递,所以不会影响外头的id:  在上图中,如果没有写mutable,就不能在里头id++。
再来看下图,传引用就会改变外部的id,而传值又不为mutable就会报错: 
 又回到了decltype的用法,用来传递lambda的type:  由于lambda的语法,如法给他构造函数或是赋值操作。所以必须要把lambda传给构造函数(如上的cmp传递给coll容器),否则就会调用默认构造函数,然后就会报错。 所以得如上这样写出:
std::set<Person, decltype(cmp)> coll(cmp);

14. Variadic Templates 1
重新回到开头:  七个大例子: 
15. Variadic Templates 2
用Variadic Templates重写printf(),很简单就实现了出来: 
16. Variadic Templates 3
参数type相同的话用initializer_list就可以了:  但是这样写max的参数必须要用{}来产生临时对象,变成initializer_list。
我的补充
这里我查看cplusplus网站: https://www.cplusplus.com/reference/algorithm/max/?kw=max  可见max有三种(这里我们以C++11的标准来看,14和11差不多),默认为两个参数,即我们平常使用的
std::cout << std::max(1, 4) << std::endl;
第一种其实不用包含头文件algorithm也能用。 而第二种和第三种则必须要包含头文件algorithm。 而第二种则是可以给一个比较方法。我们先来读读标准库源码(测试环境VS2019):  可以看到,标准库首先用了noexcept关键字(这在前面说过,如果为true则代表一定不会抛出异常)来让程序更健壮(后面注释也说了strengthened)。 然后可以看到模板参数的_Pred其实就是一个仿函数,如果为真则这个max结果返回_Right,为假则返回_Left,我写的测试程序如下:
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
pair<int, int> FirstArg = { 1, 10 };
pair<int, int> SecondArg = { 100, 2 };
pair<int, int> DefaultResult = max(FirstArg, SecondArg);
pair<int, int> CompResult = max(FirstArg, SecondArg, [](pair<int, int>a, pair<int, int>b) {return a.second < b.second; });
cout << "DefaultResult: " << DefaultResult.first << " " << DefaultResult.second << endl;
cout << "CompResult: " << CompResult.first << " " << CompResult.second << endl;
return 0;
}
程序运行结果:  可以看到,默认的是比较pair的第一个数的大小,如果第一个数相等则再比较第二个数。所以返回的是SecondArg. 而使用max的第二种方法,我们给了一个lambda表达式,用来比较两个pair的second,如果第一个的second比第二个的大就返回false,false则对应返回_Left,即这里返回的是FirstArg.达到了比较pair第二个参数的目的。 第三种方法即传入多个参数,必须要用{}包起来,测试程序如下:
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
cout << max({ 1, 2, 3, 4, 5 }) << endl;
return 0;
}
多个参数的时候必须要用{}包起来,实际上在这里编译器会将其推导为initializer_list类型,产生一个initializer_list的临时对象,然后会调用标准库中的(algorithm第9545行):  这里的_STD实际上就是::std::,接着则会调用:  可以看到,之后就是简单地在initializer_list中找到最大值就可以了。简单提一句,initializer_list的底层就是array去实现的。
17. Variadic Templates 4

18. Variadic Templates 5

19. Variadic Templates 6
前面的例子都是通过分解Args去递归调用函数。 这里用类模板来用于递归继承:  这里使用private继承,非常好:  这里正规来讲只要在这里写了::(Head::type)就要在前面写上typename,不过有的编译器不写也能过。 而这里的tuple的构造函数,除了设定m_head,还调用了父类的构造函数:inherited(vtail…) 虽然看起来逻辑很正确,却在Head::type报了错:  因为这里的Head是int、float、string,怎么能回答type呢? 所以改为:  不过其实直接拿Head来用就行了…: 
20. Variadic Templates 7
之前讲Variadic Templates用于递归继承,这里讲Variadic Templates用于递归复合:  这里Variadic Templates所有的例子都是递归:递归调用、递归创建、递归继承、递归复合
|