该博客用于记录自己在阅读过程中不懂的知识点,很少用到但比较重要的知识点以及模棱两可的知识点
在模板参数列表中class和typename含义相同
绑定到指针或引用非类型参数的实参必须具有静态生存期
编写泛型代码的两个重要原则:
模板中的函数参数是const的引用(保证了可以传递一些不能拷贝的类型,如流类型)
函数体中的条件判断仅需要<比较运算符(对函数的要求变低了只需要满足拥有<运算,更灵活)
定义模板时编译器并不会生成代码,只有生成特定版本时编译器才会生成代码,与类不同,模板实例化时函数模板和类模板成员函数必须有定义(实例化类对象时,类的成员函数的定义不必先出现)
编译模板本身时检查模板语法错误等,编译器遇到使用模板时检查参数数量和类型,模板实例化时编译器才会发现大部分错误
定义在类模板内的成员函数被隐式的声明为内联函数
模板成员函数应和模板使用相同的模板参数且定义在模板外的成员函数应该带上template <typename T>
类模板内使用该模板类不用加模板参数
template <typename T> class Pal;
class C{ //C是普通类
friend class Pal<C> //类C实例化的Pal是C的一个友元
template <typename T> friend class Pal2; //Pal2的所有实例都是C的友元
}
template <typename T> class C2{
friend class Pal<T>; //C2的每个实例将相同实例化的Pal声明为友元
template <typename X> friend class Pal2; //Pal2的所有实例都是C2的每个实例的友元
friend class Pal3; //Pal3是一个普通类,是C2所有实例的友元
}
我门可以将模板类型声明为友元
template <typename T> class bar{
friend T;
}
模板类型定义别名
template <typename T> using twin = pair<T, T>;
twin<string> test;
模板不一定只作用于类型
template<size_t M, size_t N>
int compare(const char (&)[M], const char (&)[N])
声明的模板参数名字不必与定义中相同
成员模板的类型可以通过传入的参数自动适配
模板在使用的时候才会实例化,这就意味着多个独立编译的文件可能存在多个相同的实例,这会带来额外的开销,于是就引入了显示实例化:
extern template class declaration;
template declaration;
在一个类模板的实例化定义中,所用类型必须能用于模板的所有成员函数
shared_ptr在运行时绑定删除器,unique_ptr编译时绑定
模板型参数的转换只有const的转换和数组或函数转换成对应的指针,而算术转换,派生类向基类的转换,用户定义的转换等都不能用于函数模板
可以为模板提供显示的模板实参,且模板实参是从左至右依次匹配的
template <typename T1, typename T2>
T1 func(T2); //因为T2是在参数列表里面,所以编译器会根据实参推断T2类型,但T1类型不得而知,所以使用时就需要提供显示的模板实参
auto val = func<int> func(123);
如果模板函数的返回类型和某个参数类型一致时,我们可以使用尾置返回类型,因为尾置返回类型出现之前参数就已经出现了,这样还可以减轻用户负担,使他不再需要提供显示的模板实参
标准库类型转换模板
如果一个模板形参类型为T&& 年可以传递一个左值实参,可能你会认为一个右值引用怎么可能接受一个左值实参,但事实上,编译器会推断T是int&而不是int,此时就会是一个很反常的情况,引用的引用(虽然特殊,但在这种情况下是对的)
此时还应提一点,这种特殊的引用的引用会被折叠,T& &和T& &&以及T&& &都会被折叠成T&, 而只有T&& &&才会被折叠成T&&,理解一下折叠不难发现,当参数是右值引用时,传递参数会保持参数不变
如果一个函数的参数是指向模板类型参数的右值引用,它对应的实参的const属性和左值/右值属性将得到保持,forward返回一个右值引用
sizeof...(args)返回一个参数包(模板参数包,函数参数包)中参数的数目
模板特例化本质上是一个实例,当两个模板提供同样好的匹配时,编译器会选择特例化版本
类也有特例化,并且类可以部分特例化(函数模板不行),只提供部分参数或者参数的一部分特性