|
作者:readywang(王玉龙)template是c++相当重要的组成部分,堪称c++语言的一大利器。在大大小小的c++程序中,模板无处不在。c++templates作为模板学习的经典书籍,历来被无数c++学习者所推崇。第二版书籍覆盖了c++1114和17标准,值得程序猿们精读学习,特此整理学习笔记,将每一部分自认为较为重要的部分逐条陈列,并对少数错误代码进行修改一、函数模板1.1函数模板初探1.模板实例化时,模板实参必须支持模板中类型对应的所有运算符操作。template T max(const T &a, const T &b) { return a > b? a : b;}class NdGreater {};int main() { NdGreater n1, n2; ::max(n1, n2); // 不支持 > 编译报错}2.模板编译时会进行两阶段检查a.模板定义时,进行和类型参数无关的检查,如未定义的符号等。b.模板实例化时,进行类型参数相关的检查。templatevoid foo(T t) { undeclared(); // 如果 undeclared()未定义,第一阶段就会报错,因为与模板参数无关 static_assert(sizeof(T) > 10, "T too small"); //与模板参数有关,只会在第二阶段报错}3.根据两阶段检查,模板在实例化时要看到完整定义,最简单的方法是将实现放在头文件中。1.2模板参数推断1.函数模板的模板参数可以通过传递的函数参数进行推断。2.函数推断时会用到参数类型转换,规则如下:a.如果函数参数是按引用传递的,任何类型转换都不被允许。(此处有疑问,const转换还是可以的)b.如果函数参数是按值传递的,可以进行退化(decay)转换:const(指针或者引用只有顶层const可以被忽略)和volatile被忽略;引用变为非引用;数组和函数变为对应指针类型。template void RefFunc(const T &a, const T &b){};template void NoRefFunc(T a, T b){};int main() { int *const ic = nullptr; const int *ci = nullptr; int *p = nullptr; RefFunc(p, ic); // ok 顶层const可以被忽略 T 为 int * RefFunc(p, ci); // error 底层const不可以忽略 NoRefFunc(p, ci); // error 底层const不可以忽略 int i = 0; int &ri = i; NoRefFunc(i, ri); // ok ri从int &转换为int int arr[4]; NoRefFunc(p, arr); // ok arr 被推断为int * NoRefFunc(4, 5.0); // error T 可以推断为int或double}3.上文的最后一句调用,类型推断具有二义性,无法正确实例化。可以通过以下方式解决a.类型转换:b.显式指定模板实参: NoRefFunc(static_cast(4), 5.0); // ok 类型转换 NoRefFunc(4, 5.0); // 显式指定4.函数模板无法通过默认参数推断模板参数。如果函数模板只有一个函数参数,且函数参数提供了默认值的情况,应该为模板类型参数T也提供和函数参数默认值匹配的默认类型。template void Default(T t = 0){};Default(); // error 无法推断为inttemplate void Default(T t = 0){};Default(); // ok 默认类型为int1.3多模板参数1.当函数返回类型不能或不便由函数参数类型直接推断时,可以在函数模版中新增模板参赛指定返回类型。2.c++11之后,可以通过auto+decltype+尾后返回类型推断函数模板返回类型。当函数参数为引用类型时,返回类型应该为非引用。而decltype会保留引用,因此还需通过decay进行类型退化。3.c++14之后,可以通过auto直接推断函数模板返回类型,前提是函数内部的多个返回语句推断出的返回类型要一致。auto会自动对类型进行decay。4.c++11之后,可以通过common_type返回多个模版类型参赛的公共类型,common_type返回的类型也是decay的。#include// 单独通过RT指定返回类型template RT max1(const T1& a, const T2& b) { return a > b ? a : b; }// auto c++11支持 通过decay 进行类型退化 typename 用于声明嵌套从属名称 type 为类型而不是成员template auto max2(const T1& a, const T2& b) -> typename std::decay b ? a : b)>::type { return a > b ? a : b; }// auto c++14支持template auto max3(const T1& a, const T2& b) { return a > b ? a : b; }// common_type c++11支持 max4(5, 7.3) max4(7.4, 5) 的返回类型均被推断为doubletemplate typename std::common_type::type max4(const T1& a, const T2& b) { return a > b ? a : b; }1.4默认模板参数1.可以给模板参数指定默认值。// 默认模板参赛 因为RT需要T1 T2推断,所以放在最后template ::type>RT max5(const T1& a, const T2& b) { return a > b ? a : b; }1.5函数模板重载1.一个非模板函数可以和同名的函数模板共存,并且函数模板可实例化为和非模板函数具有相同类型参数的函数。函数调用时,若匹配度相同,将优先调用非模板函数。但若显式指定模板列表,则优先调用函数模板。2.函数模板不可以进行类型自动转换,非模板函数可以。#pragma oncetemplate T max(T a, T b) { return a > b ? a : b;}template RT max(T1 a, T2 b) { return a > b ? a : b;}int max(int a, int b) { return a > b ? a : b;}int main() { ::max(6, 8); // 调用非模板函数 ::max(6, 8); // 调用函数模板 max ::max('a', 'b'); // 调用函数模板 max ::max(4, 4.0); // 通过类型转换调用非模板函数 ::max(4, 4.0); //指定了返回类型 调用max}3.调用函数模板时,必须保证函数模板已经定义。int max(int a, int b) { return a > b ? a : b;}template T max(T a, T b, T c) { return max(max(a,b),c); //T为int时,并不会调用max 而是调用非模板函数}template T max(T a, T b) { return a > b ? a : b;}max(1, 2, 3); // 最终调用非模板函数比较max("sjx", "wyl", "shh"); // error 找不到二元的max二、类模板2.1stack类模板实现1.类模板不可以定义在函数作用域或者块作用域内部,通常定义在global/namespace/类作用域。#include#includetemplate class Stack{public: void push(const T& value); void pop(); T top(); int size() const { elem_.size(); }; bool empty() const { return elem_.empty(); }; void print(std:stream & out) const;protected: std::vector elem_;};template void Stack::push(const T &value){ elem_.push_back(value);}template void Stack::pop(){ elem_.pop_back();}template T Stack::top(){ return elem_.back();}template void Stack::print(std:stream &out) const{ for (auto e : elem_) { out << e << std::endl; }}2.2 stack 类模板使用1.直到 c++17,使用类模板都需要显式指定模板参数。2.类模板的成员函数只有在调用的时候才会实例化。2.3 部分使用类模板1.类模板实例化时,模板实参只需要支持被实例化部分所有用到的操作。int main(){ // 只会实例化类模板中的push 和 print函数 Stack s; s.push(3); s.print(std::cout); // Stack未重载<<运算符,实例化print函数时失败 Stack
|
|