1. 函數模板一

 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;
}

    

相關文章
相關標籤/搜索