C++ 模板

函數模板

函數模板,是可以創(chuàng)建一個通用的函數,可以支持多種形參。

用關鍵字 template 來定義,

在函數模板中,數據的值和類型都被參數化了,發(fā)生函數調用時編譯器會根據傳入的實參來推演形參的值和類型。

template <class 類型名1,class 類型名2…>
返回值 函數名(形參表列) 模板參數表
{
    // 函數體
}

第一行的template<class 類型名1,class 類型名2…>是一句聲明語句

template 是定義模板函數的關鍵字

尖括號里可以有多個類型

用class(或者typename來定義)。

然后后面跟定義的函數模板,切記中間不可以加其他的語句,不然會報錯!

#include <iostream>
#include <stdlib.h>
using namespace std;

template <typename T>
void print(T);

int main()
{
    print(55);
    print('Y');
    print("Hello");
    system("pause");
    return 0;
}

template <typename T>
void print(T x)
{
    cout
        << x
        << endl;
}

函數模板也可以提前聲明,不過聲明時需要帶上模板頭,并且模板頭和函數定義(聲明)是一個不可分割的整體,它們可以換行,但中間不能有分號。

類模板

的定義格式:

template<typename 類型參數1 , typename 類型參數2 , …> class 類名{
    //TODO:
};

例:

template<class T1,class T2>
class Student 
{
    // 成員變量  成員函數
};

在類外定義成員函數時仍然需要帶上模板頭

成員函數定義模板格式:

template<typename 類型參數1 , typename 類型參數2 , …>
返回值類型 類名<類型參數1 , 類型參數2, ...>::函數名(形參列表){
    //TODO:
}

例:

template<class T1, class T2>
void Student<T1, T2>::say(){
};

構造函數 以及 析構函數 的模板 ,兩者類似,析構函數只需要 多添加一個 ~ 符,且沒有參數

template<typename 類型參數1 , typename 類型參數2 , …>
類名<類型參數1 , 類型參數2, ...>::類名(形參列表) : 變量初始化{
    //TODO:
}

例:

// 構造函數
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {};

// 析構函數
template<class T1, class T2>
Student<T1, T2>::~Student() {
};

指針對象 注意事項:

  • 賦值號兩邊的數據類型必須一致
  • 賦值號右邊需要指明數據類型
格式:
類名<類型參數1,類型參數2 ····> 對象名 = new 類名<類型參數1,類型參數2 ····>(初始化參數1,參數2 ·····);

例:
Student<string, string> *Man = new Student<string, string>("渾元形意太極門掌門人", "馬保國");
#include <iostream>
#include <string>

using namespace std;
template<class T1,class T2>
class Student 
{
private:
    T1 m_a;
    T2 m_b;
public:
    Student(T1 a, T2 b);
    void say();
    ~Student();
};
// 構造函數
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {};
// 成員函數
template<class T1, class T2>
void Student<T1, T2>::say(){
    cout << this->m_a << this->m_b << endl;
};
// 析構函數
template<class T1, class T2>
Student<T1, T2>::~Student() {
    cout << this->m_a << this->m_b << endl;
};

int main()
{
    Student<string,string> Sir("渾元形意太極門掌門人","馬保國");
    Sir.say();
    Student<int,char> c(2,'B');
    c.say();
    Student<string, string> *Man = new Student<string, string>("練習兩年半的偶像練習生", "坤坤");
    Man->say();
    delete Man;

    return 0;
}

結果:
渾元形意太極門掌門人馬保國
2B
練習兩年半的偶像練習生坤坤
練習兩年半的偶像練習生坤坤
2B
渾元形意太極門掌門人馬保國

類模板的靜態(tài)成員

靜態(tài)模板成員變量

static 數據類型 變量名;

初始化格式:
template<typename 類型參數1 , typename 類型參數2 , …>
數據類型 類名<類型參數1 , 類型參數2, ...>::變量名 = 數據;

注意: 數據類型定義與初始化要一致

不使用模板定義靜態(tài)變量:

static T1 count1;

// 模板靜態(tài)成員變量初始化  
template<class T1, class T2>
T1 Student<T1, T2>::count1 = 0;

使用固定類型定義靜態(tài)變量:

static int count2;

// 模板靜態(tài)成員變量初始化
template<class T1, class T2>
int Student<T1, T2>::count2 = 0;

注意:

  • 靜態(tài)成員變量,可以在類的內部使用,可以在外部進行操作

  • 靜態(tài)成員變量使用模板定義時,當傳入的模板類型不是可以操作的類型就會報錯

原本希望傳入的是整型,然后進行++操作

結果傳入了string類型,由于不匹配所以會出現以下錯誤提示:

錯誤  C2676   二進制“++”:“T1”不定義該運算符或到預定義運算符可接收的類型的轉換
#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2>
class Student
{
public:
    static T1 count;    // 計數
private:
    T1 m_a;
    T2 m_b;
public:
    Student(T1 a, T2 b);
    void say();
};

// 構造函數
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
    //count++; //傳入的類型T1 不為整數可操作類型,就會報錯, C2676
};

// 成員函數
template<class T1, class T2>
void Student<T1, T2>::say() {
    cout  << ". " << this->m_a << this->m_b << endl;
    
    // 靜態(tài)成員變量初始化之后,在類中使用時,會引發(fā)異常  此位置的異常 : return (_CSTD strlen(_First));
    //cout << this->count << endl;
};

template<class T1, class T2>
T1 Student<T1, T2>::count = 0;// 僅初始化,不在類中使用,就不會出現問題,但也就好無意義了

int main()
{
    Student<string, string> Sir("渾元形意太極門掌門人", "馬保國");
    // 因為傳入了string類型,count可以手動賦值來計數,但無法通過,會引發(fā)異常, 此位置的異常 : return (_CSTD strlen(_First));
    // Sir.count = "1";
    Sir.say();
    return 0;
}

引起一系列問題的原因可能為:

1. 設置的類型初始化問題
    不能匹配確定是否合理初始化
    
2. 設置類型與想要操作的類型不匹配
    希望使用 ++等一些列運算符 操作的 整數或浮點數類型
    時間類型為 string

傳入類型合理類型,修改上例的相關區(qū)域:

// 構造函數
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
    count++; 
};
// 成員函數
template<class T1, class T2>
void Student<T1, T2>::say() {
    cout << ". " << this->m_a << this->m_b << endl;
    cout << this->count << endl;
};

// 初始化
template<class T1, class T2>
T1 Student<T1, T2>::count = 0;


// 創(chuàng)建對象
Student<int, string> Sir(2,"B" );
Sir.count++;
Sir.say();


結果:
. 2B
2

因此解決此類問題,就可以為static類型變量,創(chuàng)建 特定的構造函數 以及 模板

#include <iostream>
#include <string>

using namespace std;

// T3專門用來給靜態(tài)變量傳遞模板數據類型
template<class T1, class T2,class T3>
class Student
{
public:
    static T3 count;   // 計數
private:
    T1 m_a;
    T2 m_b;
public:
    Student(T1 a, T2 b);
    void say();
};

// 構造函數,可以設置空的構造函數,之后通過接口傳值,此處就省略
template<class T1, class T2,class T3>
Student<T1, T2, T3>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
    count++;
};
// 成員函數
template<class T1, class T2, class T3>
void Student<T1, T2, T3>::say() {
    cout << this->count  << ". " << this->m_a << this->m_b << endl;
};

// 靜態(tài)成員變量的初始化
template<class T1, class T2, class T3>
T3 Student<T1, T2, T3>::count = 0;

int main()
{
    Student<string, string,int> Sir("渾元形意太極門掌門人", "馬保國");
    Sir.say();

    Student<string, string,int> *Man = new Student<string, string,int>("練習兩年半的偶像練習生", "坤坤");
    Man->say();
    delete Man;

    Student<int, char,int> c(2, 'B');
    c.say();    
    Student<int, char,int> l(3, 'B');
    l.say();    
    Student<int, char,int> s(6, 'B');
    s.say();
    return 0;
}

結果:
1-1. 渾元形意太極門掌門人馬保國
2-2. 練習兩年半的偶像練習生坤坤
1-1. 2B
2-2. 3B
3-3. 6B
  • 通過靜態(tài)變量可以知道,每次類的模板不同,就會產生新的類,且不互通

上例產生了兩個類的格式

  • Student<int, char,int>
  • Student<string, string,int>

類模板與友元函數

// 模板類
template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
// 構造函數
    Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函數
    friend T sum(Student<T> s);
};

// 函數模板
template<typename T>
T sum(Student<T> s)
{
    return s.m_x + s.m_y;
}

嚴重性 代碼  說明  項目  文件  行   禁止顯示狀態(tài)
錯誤  LNK2019 無法解析的外部符號 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl sum(class Student<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >)" (?sum@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$Student@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@@Z),該符號在函數 "void __cdecl Study(void)" (?Study@@YAXXZ) 中被引用 學習  E:\C++\學習\學習\Study.obj  1   

類模板 不能 引入函數模板,雖然程序沒有錯誤,但是在編譯時就會出錯

友元函數 需要指明 具體的參數類型 ,可以通過 重載 去定義多個 友元函數

注意: 友元函數 無法直接 訪問類模板的參數,需要傳遞 類的對象對象指針

#include <iostream>
#include <string>

using namespace std;

// 模板類
template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
// 構造函數
    Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函數的聲明
    friend string sum(Student<string> s);
    friend int sum(Student<int> s);
};

// 友元函數的定義
string sum(Student<string> s)
{
    return s.m_x + s.m_y;
}
int sum(Student<int> s)
{
    return s.m_x + s.m_y;
}

int main()
{
    Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
    cout << sum(Sir) << endl;
    
    Student<int> Age(6,9);
    cout << sum(Age) << endl;
    return 0;
}

類模板與 友元 類的成員函數

friend 表示 出現 friend 的類

member 表示 成員函數的類

定義的一般順序:

第一步:
class friend;

第二步:
template <class T1, ···· >
class member{
    // 成員變量 , 成員函數
    // 需要 友元的成員函數 聲明
}

第三步:
class friend{
    // 成員變量 , 成員函數
    // friend member類的 友元成員函數
}

第四步:
member類的 友元成員函數 的實現

例子:

#include <iostream>
#include <string>

using namespace std;

template<typename T>
class Student;      // 同樣很有必要的聲明


// 第一個類, member類
class Introduct
{
public:
    // 成員函數
    string sum(Student<string>* s); // 對象指針
    int sum(Student<int> s);        // 對象
};

// 第二個類,友元類
template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
// 構造函數
    Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函數的聲明
    friend string Introduct::sum(Student<string>* s);
    friend int Introduct::sum(Student<int> s);
};


// 友元函數的定義
string Introduct::sum(Student<string>* s)
{
    return s->m_x + s->m_y;
}
int Introduct::sum(Student<int> s)
{
    return s.m_x + s.m_y;
}


int main()
{
    Introduct Mk;

    Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
    cout << Mk.sum(&Sir) << endl;

    Student<int> Age(6,9);
    cout << Mk.sum(Age) << endl;
    return 0;
}

結果:
渾圓形意太極門掌門人馬保國
15

通過 重載 來擴展, member類的使用

同樣需要傳遞 實例對象 或 實例對象指針,訪問類的成員

類模板與 友元 模板類的成員函數

兩個類模板之間

  1. 提前聲明 member類
  2. 定義 friend類, 引入友元的成員函數
  3. 定義 member類
  4. 定義 模板成員函數
#include <iostream>
#include <string>

using namespace std;

template<typename T>
class Introduct;        // 有必要的提前定義

// 第一個類, 友元類
template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
    Student(T x, T y) :m_x(x), m_y(y) {};
    // 友元類
    friend T Introduct<T>::sum(Student<T> s);
};


// 第二個類, member類
template<typename S>
class Introduct
{
public:
    // 成員函數
    S sum(Student<S> s);
};

// 模板成員函數
template<typename S>
S Introduct<S>::sum(Student<S> s)
{
    return s.m_x + s.m_y;
}

int main()
{
    Introduct<string> Mk;
    Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
    cout << Mk.sum(Sir) << endl;

    Introduct<int> M;
    Student<int> Age(6, 9);
    cout << M.sum(Age) << endl;
    return 0;
}

注意:
在使用模板時,前后要對應

類模板 與 友元 類

優(yōu)先定義 友元類

#include <iostream>
#include <string>

using namespace std;

// 第一個類, 友元類
template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
    Student(T x, T y) :m_x(x), m_y(y) {};
    // 友元類
    friend class Introduct;
};


// 第二個類, member類
class Introduct
{
public:
    // 成員函數
    string sum(Student<string>* s); // 對象指針
    int sum(Student<int> s);        // 對象
};

// Introduct成員函數
string Introduct::sum(Student<string>* s)
{
    return s->m_x + s->m_y;
}
int Introduct::sum(Student<int> s)
{
    return s.m_x + s.m_y;
}

int main()
{
    
    Introduct Mk;

    Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
    cout << Mk.sum(&Sir) << endl;

    Student<int> Age(6,9);
    cout << Mk.sum(Age) << endl;
    return 0;
}

結果:
渾圓形意太極門掌門人馬保國
15

類模板 與 友元類類模板與 類的友元成員函數 類似,不同點就是前者無需過多聲明 成員函數,后者需要依次 聲明 需要用到的 成員函數

類模板 與 友元 類模板

兩個 類模板

  1. 提前聲明 member類
  2. 定義 friend類 , 引入 友元類
  3. 定義 member類
  4. 定義 模板成員函數
#include <iostream>
#include <string>

using namespace std;

template<typename T>
class Introduct;        // 很有必要的聲明

// 第一個類, 友元類
template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
    Student(T x, T y) :m_x(x), m_y(y) {};
    // 友元類
    friend class Introduct<T>;
};


// 第二個類, member類
template<typename S>
class Introduct
{
public:
    // 成員函數
    S sum(Student<S> s);
};

template<typename S>
S Introduct<S>::sum(Student<S> s)
{
    return s.m_x + s.m_y;
}


int main()
{
    
    Introduct<string> Mk;
    Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
    cout << Mk.sum(Sir) << endl;

    Introduct<int> M;
    Student<int> Age(6, 9);
    cout << M.sum(Age) << endl;
    
    return 0;
}

結果:
渾圓形意太極門掌門人馬保國
15

類模板與非類型參數

在定義模板的時候,可以引入參數

不能使用 結構體 、string 等定義非類型參數

template <class T, 數據類型 參數名, ······ >

例如:
template <class T1 , int count , class T2>
class Student
{
}

在傳遞參數時,需要傳遞相對應的數據類型的數據
Student<int, 2 ,char> Sir;

例如:

#include <iostream>
#include <string>

using namespace std;

// 模板類
template<typename T, int count>
class Student
{
private:
    T m_n;
public:
    // 構造函數
    Student(T n) :m_n(n){};
    T say()
    {
        m_n += count;
        return m_n;
    }
};

// 

void Study()
{
    Student<int,2> Sir(1);
    cout << Sir.say() << endl;
    cout << Sir.say() << endl;
    cout << Sir.say() << endl;
    cout << Sir.say() << endl;
}

結果:
3
5
7
9

類模板的繼承

基類模板 與 子類

#include <iostream>
#include <string>

using namespace std;

// 基類
template <class T>
class People
{
public:
    T m_num;
public:
    // 構造函數
    People(T num) : m_num(num) {};
    // 成員變量
    void show()
    {
        cout << "num = " << this->m_num << endl;
    }
};

// 子類
class Student : public People<int> {
private:
    int m_code;
public:
    // 構造函數
    Student(int code, int num) :m_code(code), People<int>(num) {};
    // 成員變量
    void show()
    {
        cout << "code = " << this->m_code << ";"
             << "num = " << this->m_num << endl;
    }
};


int main()
{
    People<int> sir_1(10);
    sir_1.show();

    Student sir_2(996, 777);
    sir_2.show();
    
    return 0;
}

結果:
num = 10
code = 996;num = 777

基類模板 與 子類模板

#include <iostream>
#include <string>

using namespace std;

// 基類
template <class T>
class People
{
public:
    T m_num;
public:
    // 構造函數
    People(T num) : m_num(num) {};
    // 成員變量
    void show()
    {
        cout << "num = " << this->m_num << endl;
    }
};

// 派生類
template <class S>
class Student : public People<S> {
private:
    S m_code;
public:
    // 構造函數
    Student(S code, S num) :m_code(code), People<S>(num) {};
    // 成員變量
    void show()
    {
        cout << "code = " << this->m_code << ";"
             << "num = " << this->m_num << endl;
    }
};


int main()
{
    People<int> sir_1(10);
    sir_1.show();

    Student<int> sir_2(996, 777);
    sir_2.show();
    
    return 0;
}

結果:
num = 10
code = 996;num = 777

注意:構造函數,基類要指明類型,繼承是也要指明類型,且前后照應

模板實參推斷

從函數實參來確定模板實參的過程被稱為模板實參推斷(template argument deduction)

使用函數模板顯示實參,可以覆蓋實參推斷機制

#include <iostream>
#include <string>

using namespace std;

template<typename T1, typename T2>
int max(T1 a, T2 b)
{
    cout
        << "T1 = " << sizeof(T1) << endl
        << "T2 = " << sizeof(T2) << endl;
        
    if (a > b) return a;
    else if (a == b) return 0;
    else return b;
}

int main()
{
    short sh = 6;
    
    // 系統(tǒng)推斷
    cout << max(sh, 3) << endl; 
    // 顯式定義
    cout << max<int>(sh, 3) << endl; 
    cout << max<int,int>(sh, 3) << endl;
    return 0;
}

結果:
T1 = 4
T2 = 2
6
T1 = 4
T2 = 2
6
T1 = 4
T2 = 4
6

模板實例化

模板實例化是生成采用特定模板參數組合的具體類或函數(實例)

編譯器生成一個采用 Array<int> 的類,另外生成一個采用 Array<double> 的類。通過用模板參數替換模板類定義中的模板參數,可以定義這些新的類。

隱式實例化

隱式實例化:通過編譯器自己推測判斷要實例化的類型。

編譯器會根據實參推斷類型

顯式實例化

模板顯式實例化(extern template)可以用來確保模板只被編譯器實例化一次

  • 使用模板生成的編譯單元不會重復實例化,會加快編譯速度,并減小編譯單元的尺寸

要顯式實例化模板函數,在 template 關鍵字后接函數的聲明(不是定義),且函數標識符后接模板參數。

template 返回值類型 函數名 <數據類型>

例如:
template<typename T1, typename T2>
T1 max(T1 a, T2 b)
{
    if (a > b) return a;
    else if (a == b) return 0;
    else return b;
}

template int max<int,int>(int a, int b);  //顯式實例化,只需聲明

模板類的顯式實例化

template class 類名<數據類型>

例如:
template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
    Student(T x,T y) :m_x(x), m_y(y) {};
    void prin()
    {
        cout << m_x << m_y << endl;
    }
};

// 顯式實例化 類模板
template class Student<char>;
template class Student<string>;

模板類靜態(tài)數據成員的顯式實例

template 返回值類型 類名<數據類型>::函數名(參數列表);

例如:
template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
    Student(T x,T y) :m_x(x), m_y(y) {};
    T sum(T a);
};

template<typename T>
T Student<T>::sum(T a)
{
    return this->m_x + this->m_y + a;
}

// 顯式實例化 類模板
template string Student<string>::sum(string a);
template int Student<int>::sum(int a);

注意:類型的規(guī)范,使用相同模板在帶入數據類型時,也要同步,不要隨心所欲,想使用不同類型就分開定義,一句話:早知現在何必當初

模板顯式具體化

讓模板能夠針對某種具體的類型使用不同的算法(函數體或類體不同),稱為模板的顯示具體化(Explicit Specialization)

利用對函數模板的顯式具體化,對于 數組結構體 數據類型進行操作

C++98標準 : 原型和定義以 template<> 開頭,并通過名稱指出類型。函數調用優(yōu)先級是 非模板函數 > 具體化模板函數 > 常規(guī)模板函數

函數模板類模板 的基礎上,新添加一個專門針對 特定類型 的、實現方式不同具體化 函數或類

模板函數顯式具體化

template<> 
數據類型 函數名<復雜數據類型>(參數列表){
    // 函數體
}

例如:
template <class T>
T stripling(T a, T b)
{
    return a < b ? a : b;
};

// 只有定力了原型模板,才能定義如下的顯式具體化,原型模板如上 template <class T> T stripling(T a, T b)
// People 為定義的結構體類型
template <>
People stripling<People>(People a, People b)
{
    return a.age < b.age ? a : b;
};
#include <iostream>
#include <string>

using namespace std;

typedef struct{
    string name;
    int age;
}People;

// 輸出 較大值
template <class T>
T stripling(T a, T b)
{
    return a < b ? a : b;
};

// 需要有原型,如上 template <class T> T stripling(T a, T b)
template <>
People stripling<People>(People a, People b)
{
    return a.age < b.age ? a : b;
};

void prin(People sir)
{
    cout << sir.name << "," << sir.age << "歲,不講武德" << endl;
}

int main()
{
    People young1 = { "八十公斤的年輕人",35 };
    People young2 = { "九十公斤的年輕人",30 };
    People leader = { "馬保國",69 };
    prin(stripling(young1,leader));
    prin(stripling(young2,leader));
    return 0;
}

模板類的顯式具體化

在類模板的具體化中,成員方法的實例化是不能帶模板頭template<>的。

template<>
class 類名<復雜數據類型>
{
    // 成員變量
    // 成員函數
}

例如:
template <class T1, class T2>
class People {
    // 成員變量 成員函數
}

// msg 為 結構體
template<>
class People<msg , msg>
{
    // 成員變量 成員函數
}
#include <iostream>
#include <string>

using namespace std;

template <class T1, class T2>
class People
{
private:
// 成員變量
    T1 m_x;
    T2 m_y;
public:
// 構造函數
    People(T1 x, T2 y) :m_x(x), m_y(y) {};
// 成員函數
    void show();
};

template <class T1, class T2>
void People<T1,T2>::show()
{
    cout << m_x << "," << m_y << endl;
}

// 類模板顯式具體化(針對 結構體 的顯式具體化)
typedef struct
{
    string name;
    int age;
} msg ;

template<>
class People<msg , msg>
{
private:
// 成員變量
    msg m_leader;
    msg m_young;
public:
// 構造函數
    People(msg leader,msg young) :m_leader(leader), m_young(young){};
// 成員函數
    void show();
};

// 注意!這里不能帶模板頭template<>
void People<msg, msg>::show()
{
// 判斷 年輕人 與 掌門人 年齡
    msg stripling = this->m_leader.age > this->m_young.age ? m_leader : m_young;
// 輸出年齡大的老同志
    cout << stripling.name << ":" << stripling.age << endl << "馬家功夫名不虛傳" << endl;
}


int main()
{
    People<string, string> Sir("渾圓形意太極門掌門人","馬保國");
    Sir.show();

    msg young = { "八十公斤的年輕人",35 };
    msg leader = { "馬保國",69 };
    People<msg, msg> Man(leader,young);
    Man.show();
    return 0;
}

結果:
渾圓形意太極門掌門人,馬保國
馬保國:69
馬家功夫名不虛傳

部分顯式具體化

部分顯式具體化只能用于類模板,不能用于函數模板

template<class 模板類型>
class 類名<復雜數據類型 , 模板類型>         // 模板類型 要 對應,位置不固定,根據情況而定
{
    // 成員變量
    // 成員函數
}

例如:
template <class T1, class T2>
class People {
    // 成員變量 成員函數
}

// msg 為 結構體
template<class T>
class People<msg , T>
{
    // 成員變量 成員函數
}
#include <iostream>
#include <string>

using namespace std;

template <class T1, class T2>
class People
{
private:
// 成員變量
    T1 m_x;
    T2 m_y;
public:
// 構造函數
    People(T1 x, T2 y) :m_x(x), m_y(y) {};
// 成員函數
    void show();
};

template <class T1, class T2>
void People<T1,T2>::show()
{
// 輸出數據
    cout << m_x << "," << m_y << endl;
}

// 部分顯式具體化(針對 結構體 的部分顯式具體化)
typedef struct
{
    string name;
    int age;
}msg;

template<typename T>
class People<msg , T>
{
private:
// 成員變量
    msg m_leader;
    T m_sure_age;
public:
// 構造函數
    People(msg leader,T sure_age) :m_leader(leader), m_sure_age(sure_age){};
// 成員函數
    void show();
};

// 注意!需要帶模板頭
template<typename T>
void People<msg, T>::show()
{
// 修改數據,并輸出
    this->m_leader.age = this->m_sure_age;
    cout << this->m_leader.name << ":" << this->m_leader.age << endl << "馬家功夫名不虛傳" << endl;
}


int main()
{
    People<string, string> Sir("渾圓形意太極門掌門人","馬保國");
    Sir.show();

    msg leader = { "馬保國",35 };
    People<msg, int> Man(leader,69);
    Man.show();
    
    return 0;
}

結果:
渾圓形意太極門掌門人,馬保國
馬保國:69
馬家功夫名不虛傳

模板 用于多文件編程

在將函數用于多文件編程時,我們通常是將 函數定義 放在 源文件(.cpp 文件) 中,將 函數聲明 放在 頭文件(.h文件)中,使用函數時 引入(#include 命令) 對應的 頭文件 即可

編譯是針對單個源文件的,只要有函數聲明,編譯器就能知道函數調用是否正確;而將函數調用和函數定義對應起來的過程,可以延遲到鏈接時期。正是有了連接器的存在,函數聲明和函數定義的分離才得以實現。

模板并不是真正的函數或類,它僅僅是用來生成函數或類的一張 “圖紙”,在這個生成過程中有三點需要明確:

  • 模板的實例化是按需進行的,用到哪個類型就生成針對哪個類型的函數或類,不會提前生成過多的代碼
  • 模板的實例化是由編譯器完成的,而不是由鏈接器完成的
  • 在實例化過程中需要知道模板的所有細節(jié),包含聲明和定義(只有一個聲明是不夠的,可能會在鏈接階段才發(fā)現錯誤)

頭文件:s.h ,存放類模板的定義

#ifndef s
#define s

template<typename T>
class Student
{
private:
    T m_x;
    T m_y;
public:
    Student(T x, T y) :m_x(x), m_y(y) {};
    T sum();
};

#endif

cpp文件,定義類的成員函數

#include <iostream>
#include <string>
#include "s.h"

using namespace std;

template<typename T>
T Student<T>::sum()
{
    return this->m_x + this->m_y ;
}

main函數調用

#include "s.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
    Student<int> Sir(1, 3);
    cout << Sir.sum() << endl;

    system("pause");
    return 0;
}

直接就報錯

嚴重性 代碼  說明  項目  文件  行   禁止顯示狀態(tài)
錯誤  LNK2019 無法解析的外部符號 "public: int __thiscall Student<int>::sum(void)" (?sum@?$Student@H@@QAEHXZ),該符號在函數 _main 中被引用 學習  E:\C++\學習\學習\學習.obj 1   

錯誤  LNK1120 1 個無法解析的外部命令    學習  E:\C++\學習\Debug\學習.exe  1   

但如果將 類的成員函數 定義放到 頭文件:s.h ,這些錯誤就沒有了

不能將模板的聲明和定義分散到多個文件中的根本原因是:模板的實例化是由編譯器完成的,而不是由鏈接器完成的,這可能會導致在鏈接期間找不到對應的模板實例。

參考:

  1. 將 C++ 模板應用于多文件編程

由于參考內容過多,在這里就不一一列舉,如若有問題,請聯系我,會及時修改,添加?。?!

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 背景 C++ 是很強大,有各種特性來提高代碼的可重用性,有助于減少開發(fā)的代碼量和工作量。 C++ 提高代碼的可重用...
    小林coding閱讀 371評論 0 3
  • C++ 模板簡介 一、模板 使用模板的目的就是能夠讓程序員編寫與類型無關的代碼。 模板是一種對類型進行參數化的工具...
    MinoyJet閱讀 2,541評論 0 12
  • 泛型:指在多種數據類型上皆可操作的含義。泛型編程的代表作品STL是一種高效、泛型、可交互操作的軟件組件 泛型編程最...
    編程小世界閱讀 221評論 0 0
  • 第五章 C++模板 5.1 模板概論 c++提供了函數模板(function template.)所謂函數模板,實...
    CodeDove閱讀 322評論 0 1
  • 一、概述 二、模板定義與使用 ?1. 函數模板的定義與使用?2. 類模板的定義與使用 三、typename 的特殊...
    從不中二的憂傷閱讀 1,736評論 1 1

友情鏈接更多精彩內容