1. 函數模板示例:ios
#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; }
此時調用了模板,編譯不經過:spa
#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. 模板接口:code
#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 數據類型改變,對接口沒有影響:blog
#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('\n'); 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; }