函数模板
函数模板,是可以创建一个通用的函数,可以支持多种形参。
用关键字 template
来定义,
在函数模板中,数据的值和类型都被参数化了,发生函数调用时编译器会根据传入的实参来推演形参的值和类型。
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
include
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();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>::
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
浑元形意太极门掌门人马保国
### 类模板的静态成员 #### 静态模板成员变量
static 数据类型 变量名;
初始化格式:
template<typename 类型参数1 , typename 类型参数2 , …>
数据类型 类名<类型参数1 , 类型参数2, ...>::变量名 = 数据;
注意: 数据类型定义与初始化要一致
不使用模板定义静态变量:
static T1 count1;
// 模板静态成员变量初始化
template<class T1, class T2>
T1 Student<T1, T2>::count1 = 0;
使用固定类型定义静态变量:
static int count2;
// 模板静态成员变量初始化
template<class T1, class T2>
int Student<T1, T2>::count2 = 0;
注意: - 静态成员变量,可以在类的内部使用,可以在外部进行操作 - 静态成员变量使用模板定义时,当传入的模板类型不是可以操作的类型就会报错
原本希望传入的是整型,然后进行++操作
结果传入了string类型,由于不匹配所以会出现以下错误提示:
错误 C2676 二进制“++”:“T1”不定义该运算符或到预定义运算符可接收的类型的转换
include
include
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;
// 静态成员变量初始化之后,在类中使用时,会引发异常 此位置的异常 : 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可以手动赋值来计数,但无法通过,会引发异常, 此位置的异常 : return (_CSTD strlen(_First));
// Sir.count = "1";
Sir.say();
return 0;
}
引起一系列问题的原因可能为:
设置的类型初始化问题
不能匹配确定是否合理初始化设置类型与想要操作的类型不匹配
希望使用 ++等一些列运算符 操作的 整数或浮点数类型
时间类型为 string
传入类型合理类型,修改上例的相关区域:
// 构造函数 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; // 创建对象 Student<int, string> Sir(2,"B" ); Sir.count++; Sir.say(); 结果: . 2B 2
因此解决此类问题,就可以为static类型变量,创建 特定的构造函数 以及 模板
#include <iostream> #include <string> using namespace std; // T3专门用来给静态变量传递模板数据类型 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; }; // 静态成员变量的初始化 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
- 通过静态变量可以知道,每次类的模板不同,就会产生新的类,且不互通
上例产生了两个类的格式
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; } 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 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类的使用
同样需要传递 实例对象 或 实例对象指针,访问类的成员
类模板与 友元 模板类的成员函数
两个类模板之间
- 提前声明 member类
- 定义 friend类, 引入友元的成员函数
- 定义 member类
- 定义 模板成员函数
#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; }
注意:
在使用模板时,前后要对应
类模板 与 友元 类
优先定义 友元类
#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
类模板 与 友元类
和 类模板与 类的友元成员函数
类似,不同点就是前者无需过多声明 成员函数,后者需要依次 声明 需要用到的 成员函数
类模板 与 友元 类模板
两个 类模板
- 提前声明 member类
- 定义 friend类 , 引入 友元类
- 定义 member类
- 定义 模板成员函数
#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; // 系统推断 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> 的类。通过用模板参数替换模板类定义中的模板参数,可以定义这些新的类。</double></int>
隐式实例化
隐式实例化:通过编译器自己推测判断要实例化的类型。
编译器会根据实参推断类型
显式实例化
模板显式实例化(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>;
模板类静态数据成员的显式实例
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);
注意:类型的规范,使用相同模板在带入数据类型时,也要同步,不要随心所欲,想使用不同类型就分开定义,一句话:早知现在何必当初
模板显式具体化
让模板能够针对某种具体的类型使用不同的算法(函数体或类体不同),称为模板的显示具体化(Explicit Specialization)
利用对函数模板的显式具体化,对于 数组
和 结构体
数据类型进行操作
C++98标准 : 原型和定义以 template<>
开头,并通过名称指出类型。函数调用优先级是 非模板函数 > 具体化模板函数 > 常规模板函数。
在函数模板 或 类模板 的基础上,新添加一个专门针对 特定类型 的、实现方式不同 的 具体化 函数或类
模板函数显式具体化
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 命令) 对应的 头文件 即可
编译是针对单个源文件的,只要有函数声明,编译器就能知道函数调用是否正确;而将函数调用和函数定义对应起来的过程,可以延迟到链接时期。正是有了连接器的存在,函数声明和函数定义的分离才得以实现。
模板并不是真正的函数或类,它仅仅是用来生成函数或类的一张 “图纸”,在这个生成过程中有三点需要明确:
- 模板的实例化是按需进行的,用到哪个类型就生成针对哪个类型的函数或类,不会提前生成过多的代码
- 模板的实例化是由编译器完成的,而不是由链接器完成的
- 在实例化过程中需要知道模板的所有细节,包含声明和定义(只有一个声明是不够的,可能会在链接阶段才发现错误)
头文件: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; }
直接就报错
严重性 代码 说明 项目 文件 行 禁止显示状态 错误 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
,这些错误就没有了
不能将模板的声明和定义分散到多个文件中的根本原因是:模板的实例化是由编译器完成的,而不是由链接器完成的,这可能会导致在链接期间找不到对应的模板实例。
参考:
由于参考内容过多,在这里就不一一列举,如若有问题,请联系我,会及时修改,添加!!!