16.1 定义模板
- 模板定义以关键字template开始,后跟一个模板参数列表,以逗号分隔,可以传一个或多个模板参数(T1,T2),模板参数列表不能为空;
- 编译器用推断出的模板参数来生成一个版本的函数叫做实例化,生成的版本叫做模板的实例;
- 模板类型参数前面必须使用关键字class或typename,在同一个模板参数列表中两个关键字可以混合使用,没有什么不同;
- 非类型模板参数的模板实参必须是常量表达式;
- inline或constexpr的函数模板中,inline和constexpr关键字说明符放在模板参数列表之后,返回类型之前
template<typename T> inline T min(const T& t1, const T &T2) {/* */}
-
泛型编码的两个重要原则:a模板中的函数参数是const的引用,b函数体中尽量减少对实参类型的要求;
-
函数模板和类模板成员函数的定义通常放在头文件中;而非模板类或模板函数不必给出定义,因此类定义和函数声明放在头文件中,普通函数和类的成员函数的定义放在源文件中,与函数模板和类模板成员函数不同;
-
类模板的成员函数本身是一个普通函数,但是,类模板的每个实例都有其自己版本的成员函数,因此定义在类模板之外的成员函数就必须以关键字template开始,后接类模板参数列表;
-
默认情况下,对于一个实例化了的类模板,其成员只有在使用时才被实例化;
-
在一个类模板的作用域内(即在类模板内)定义成员函数可以直接使用模板名而不必指定模板实参,但在类模板外定义成员函数,除了用作用域运算符::指定类外还要指定其模板实参;
-
一对一友好关系的声明方法,详见589页,为了让所有实例成为友元,友元声明中必须使用与类模板本身不同的模板参数;
-
一个static成员函数只有在使用时才会实例化;
-
当我们希望通知编译器一个名字表示类型时,必须使用关键字typename,而不能使用class;
-
当我们在类模板外定义一个成员模板时,必须同时为类模板和成员模板提供模板参数列表,类模板的参数列表在前,后跟成员自己的模板参数列表;
16.2 模板实参推断
- 将实参传递给带模板类型的函数形参时,能够自动应用的类型转换只有const转换及数组或函数到指针的转换;
template<typename T> T fobj(T,T); template<typename T> T fref(const T&, const T&); int a[10], b[42]; fobj(a,b) //可行,调用 fobj(int*,int*) fref(a,b) //不可行,形参为引用时,数组不会转换为指针,因此a,b类型不匹配 //实际上调用的是fref(const int(&a)[10], const int(&b)[42])
-
如果函数参数类型不是模板参数,则对实参进行正常的类型转换;
-
显示指定模板实参
template<typename t1, typename t2, typename t3> t3 alternative_sum(t2,t1) //声明OK,但是实例化就必须为三个模板参数指定实参 auto val3 = alternative<long long> (i, lng) //错误,t1为long long类型,另外两个无法推测
正常类型转换可以应用于显式指定的实参,详见604页;
-
尾置返回类型处理某些模板返回值类型的确定问题
template <typename It> //It为某容器迭代器类型,要求返回容器内类的引用 auto &fcn(It beg, It end) ->decltype (*beg) //尾置返回类型声明方法 { return *beg; }
-
进行类型转换的标准库模板类,用法列表(包含remove_reference, add_const等),606页;
-
引用折叠只能应用于间接创建的引用的引用,如类型别名或模板参数,有一下折叠方式
X& &, X& && , X&& &都折叠成类型X& //第一个是引用的引用,第二个是右值引用的引用,第三个是引用的右值引用 X&& &&折叠成 X&& //右值引用的右值引用折叠成右值引用
16.3 重载与模板
过于抽象,学一段时间再回来补课