1. 函数模板示例:
#include <iostream> using namespace std; //add函数 CPP中是函数的重载 /* int add(int a, int b) { return a + b; } char add(char a, char b) { return a + b; } double add(double a, double b) { return a + b; } */ template<class T> T add(T a, T b) { return a + b; } int main() { cout << add(1, 2) << endl; //3 cout << add('1', '2') << endl; //c '1'->48 '2'->49 ==>99(c) system("pause"); return 0; }
1.1 若程序变为如下:
#include <iostream> using namespace std; //add函数 CPP中是函数的重载 int add(int a, int b) { cout << "int:"; return a + b; } /* char add(char a, char b) { return a + b; } double add(double a, double b) { return a + b; } */ template<class T> T add(T a, T b) { cout << "T:"; return a + b; } int main() { cout << add(1, 2) << endl; //3 原生函数优先于模板函数 cout << add('1', '2') << endl; //c '1'->48 '2'->49 ==>99(c) system("pause"); return 0; }
1.2 明确模板数据类型(强行调用模板):
#include <iostream> using namespace std; //add函数 CPP中是函数的重载 int add(int a, int b) { cout << "int:"; return a + b; } /* char add(char a, char b) { return a + b; } double add(double a, double b) { return a + b; } */ template<class T> T add(T a, T b) { cout << "T:"; return a + b; } int main() { //cout << add(1, 2) << endl; //3 原生函数优先于模板函数 cout << add<int>(1, 2) << endl; //明确指定调用模板类型 cout << add('1', '2') << endl; //c '1'->48 '2'->49 ==>99(c) system("pause"); return 0; }
1.3 模板特性:调用才编译,不调用不编译
#include <iostream> using namespace std; //add函数 CPP中是函数的重载 int add(int a, int b) { cout << "int:"; return a + b; } /* char add(char a, char b) { return a + b; } double add(double a, double b) { return a + b; } */ template<class T> T add(T a, T b) { cout << "T:" //去掉了分号";",编译是否成功呢? return a + b; } int main() { //cout << add(1, 2) << endl; //3 原生函数优先于模板函数 cout << add<int>(1, 2) << endl; //明确指定调用模板类型 cout << add('1', '2') << endl; //c '1'->48 '2'->49 ==>99(c) system("pause"); return 0; }
此时调用了模板,编译不通过:
#include <iostream> using namespace std; //add函数 CPP中是函数的重载 int add(int a, int b) { cout << "int:"; return a + b; } /* char add(char a, char b) { return a + b; } double add(double a, double b) { return a + b; } */ template<class T> T add(T a, T b) { cout << "T:" //去掉了分号";",编译是否成功呢? return a + b; } int main() { //cout << add(1, 2) << endl; //3 原生函数优先于模板函数 //cout << add<int>(1, 2) << endl; //明确指定调用模板类型 //cout << add('1', '2') << endl; //c '1'->48 '2'->49 ==>99(c) system("pause"); return 0; }
没有调用模板,编译通过:
2. 模板接口:
#include <iostream> using namespace std; void show(int num) { cout << num << endl; } void show1(int num) { cout << num + 1 << endl; } //泛型接口:传递任何数据类型,传递任何函数指针都可以 template<class T,class F> //原则上T代表数据类型,F代表函数 void run(T t,F f) { f(t); } int main() { run(10, show); run(10, show1); system("pause"); return 0; }
2.1 数据类型改变,对接口没有影响:
#include <iostream> using namespace std; void show(int num) { cout << num << endl; } void show1(double num) //改变数据类型 { cout << num + 1 << endl; } //泛型接口:传递任何数据类型,传递任何函数指针都可以 template<class T,class F> //原则上T代表数据类型,F代表函数 void run(T t,F f) { f(t); } int main() { run(10, show); run(10.1, show1); system("pause"); return 0; }
2.2 模板嵌套使用:
#include <iostream> using namespace std; void show(int num) { cout << num << endl; } void show1(double num) //改变数据类型 { cout << num + 1 << endl; } template<class T> void showit(T num) { cout << num << endl; } //泛型接口:传递任何数据类型,传递任何函数指针都可以 template<class T,class F> //原则上T代表数据类型,F代表函数 void run(T t,F f) { f(t); } int main() { //run(10, show); //run(10.1, show1); //run("abc", showit); //无法确定类型,编译不通过 run("abc", showit<const char *>); //指定数据类型 system("pause"); return 0; }
若改为如下:
int main() { //run(10, show); //run(10.1, show1); //run("abc", showit); //无法确定类型,编译不通过 //run("abc", showit<const char *>); //指定数据类型 run("abc", showit<char *>); //严格的类型匹配(C++是强类型的语言) system("pause"); return 0; }
3. 可变参数函数模板:
3.1 类型一致的情况:
#include <iostream> #include <cstdarg> //可变参数,C语言的可变参数是<stdarg.h> using namespace std; template<class T> T add(int n, T t...) //带三个点"..."表示可变参数 ==>实现n个数相加 { cout << typeid(T).name() << endl; //先显示出类型 va_list arg_ptr; //开头的指针 va_list类型为char * va_start(arg_ptr, n); //宏va_start:从arg_ptr开始读取n个数 T res(0); //初始化结果为0 for (int i = 0; i < n; i++) { res += va_arg(arg_ptr, T); //根据数据类型取出数据 } va_end(arg_ptr); //结束读取 宏va_end return res; } int main() { cout << add(4, 1, 2, 3, 4) << endl; //4个数相加 cout << add(5, 1, 2, 3, 4, 5) << endl; //5个数相加 cout << add(5, 11.1, 12.2, 13.3, 14.4, 15.5) << endl; //实数 system("pause"); return 0; }
3.2 类型不一致的情况:
#include <iostream> #include <cstdarg> //可变参数,C语言的可变参数是<stdarg.h> using namespace std; void show() //下面的递归需要有结束条件,要有函数重载 { } //参数类型不一致,个数不确定的情况 //typename比class作用域更强,没有类模板可以认为typename=class template<typename T,typename...Args> //typename...Args处理可变参数==>解决任何类型 void show(T t, Args...args) { cout << t << endl; //打印 show(args...); //递归 绝对不能省略后面的三个点"args..." } int main() { show(1, 1.2, "123", 'A'); system("pause"); return 0; }
3.3 函数参数可变参数模板实现printf : <==> 面试中常考
#include <iostream> #include <cstdarg> using namespace std; void show(const char *str) //递归终止 { cout << str; } template<typename T, typename...Args> void show(const char *str, T t, Args...args) { while (str && *str) //指针不为空,且字符串没到末尾 { if (*str == '%' && *(str + 1) != '%') //遇到的%不是连续的,形如%d,%f { ++str; //指针后移 cout << t; //打印 show(++str, args...); //继续调用 return; } else { cout << *str++; //跳过一个字符 } } } int main() { printf("%dABCDEFG%s%c%%%fXXXX", 10, "1234", '0', 1234.5); putchar(' '); show("%dABCDEFG%s%c%%%fXXXX", 10, "1234", '0', 1234.5); system("pause"); return 0; }
4. 模板别名与auto自动推理:
#include <iostream> #include <array> using namespace std; //C++14 auto功能升级,可以任意推理 //C++11 需要用->指定数据类型 template<class T1,class T2> auto add(T1 t1, T2 t2)->decltype(t1 + t2) { return t1 + t2; } template<class T> using t = T; //对模板起别名 template<class T> using tp = T *; //template<class T> typedef T* tp; //模板的别名不能使用typedef template<class T> T show(T tx) { t<int> t1(tx); //一旦使用别名,必须明确指定类型 tp<int> tp1(&tx); cout << t1 << " " << tp1 << endl; return t1; } int main() { cout << add(1.1, 2) << endl; cout << add(1, 2.1) << endl; int a = 10; cout << show(a) << endl; system("pause"); return 0; }
#include <iostream> #include <array> using namespace std; //模板的别名,用别名来优化模板的名称,只能放在类,命名空间,全局;不可以在函数内部 template<class T> using tencent = array<T, 10>; //模板的别名,类型不确定,个数确定 template<class T,int n> using decs = array<T, n>; //模板的别名,类型不确定,个数不确定 int main() { using intarray = array<int, 10>; //为模板array取了一个别名,类型确定,个数确定 array<int, 10> myint; intarray myint2; tencent<int> t1 = { 1,2,3,4,5,6,7,8,9,0 }; for (auto i:t1) { cout << i << " "; } cout << endl; decs<int,10> t2 = { 1,2,3,4,5,6,7,8,9,0 }; for (auto i : t2) { cout << i << " "; } cout << endl; system("pause"); return 0; }
5. 函数包装器:
#include <iostream> #include <functional> //函数的命名空间 using namespace std; using std::function; //函数包装器 void go() { cout << "go" << endl; } int add(int a, int b) { return a + b; } int main() { function<void(void)> fun1 = go; //function<返回值(参数)> 包装一个函数 fun1(); function<void(void)> fun2 = []() {cout << "go lambda" << endl; }; //包装一个lambda表达式 fun2(); function<int(int, int)> fun3 = add; cout << fun3(10, 19) << endl; function<int(int, int)> fun4 = [](int a, int b)->int {return a + b; }; cout << fun4(10, 19) << endl; system("pause"); return 0; }
6. 模板元:实现代码加速
#include <iostream> //#include <chrono> //#include <iomanip> using namespace std; //台阶问题,递归方法 int get50(int n) { if (n==1) { return 1; } else if (n==2) { return 2; } else { return get50(n - 1) + get50(n - 2); } } //递归会反复调用,函数等待,返回,浪费很多时间 //模板元主要实现递归加速,游戏的优化 //优点:执行速度快 缺点:编译的时候慢,代码体积会增加(把运行的时间节约在编译的时候) template<int N> struct data { enum { //递归表达式 res = data<N - 1>::res + data<N - 2>::res }; }; template<> struct data<1> { enum { res = 1 }; }; template<> struct data<2> { enum { res = 2 }; }; int main() { //int num = 40; //cout << data<num>::res << endl; //模板元只能处理常量 //auto start1 = chrono::high_resolution_clock::now(); //开始时间 cout << data<40>::res << endl; //模板元代码加速,将运行时间放到了编译中 //auto end1 = chrono::high_resolution_clock::now(); //结束时间 //__int64 duration = (end1 - start1).count(); //cout << "程序运行时间:" << setprecision(10) << duration / 1000000000.0 << "s" // << "; " << duration / 1000000.0 << "ms" // << "; " << duration / 1000.0 << "us" // << endl; //auto start2 = chrono::high_resolution_clock::now(); //开始时间 cout << get50(40) << endl; //auto end2 = chrono::high_resolution_clock::now(); //结束时间 //__int64 duration2 = (end2 - start2).count(); //cout << "程序运行时间:" << setprecision(10) << duration2 / 1000000000.0 << "s" // << "; " << duration2 / 1000000.0 << "ms" // << "; " << duration2 / 1000.0 << "us" // << endl; system("pause"); return 0; }