C++模板 你的名字 2022-12-23 00:43 125阅读 0赞 # 函数模板 # 函数模板,是可以创建一个通用的函数,可以支持多种形参。 用关键字 `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 <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 浑元形意太极门掌门人马保国 ### 类模板的静态成员 ### #### 静态模板成员变量 #### 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 <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; // 静态成员变量初始化之后,在类中使用时,会引发异常 此位置的异常 : 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; } 引起一系列问题的原因可能为: 1. 设置的类型初始化问题 不能匹配确定是否合理初始化 2. 设置类型与想要操作的类型不匹配 希望使用 ++等一些列运算符 操作的 整数或浮点数类型 时间类型为 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类的使用 同样需要传递 实例对象 或 实例对象指针,访问类的成员 #### 类模板与 友元 模板类的成员函数 #### 两个类模板之间 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; } 注意: 在使用模板时,前后要对应 ### 类模板 与 友元 类 ### 优先定义 友元类 #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; // 系统推断 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 的类,另外生成一个采用 Array 的类。通过用模板参数替换模板类定义中的模板参数,可以定义这些新的类。 ### 隐式实例化 ### 隐式实例化:通过编译器自己推测判断要实例化的类型。 编译器会根据实参推断类型 ### 显式实例化 ### 模板显式实例化(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` ,这些错误就没有了 **不能将模板的声明和定义分散到多个文件中的根本原因是:模板的实例化是由编译器完成的,而不是由链接器完成的,这可能会导致在链接期间找不到对应的模板实例。** 参考: 1. [将 C++ 模板应用于多文件编程][C_] 由于参考内容过多,在这里就不一一列举,如若有问题,请联系我,会及时修改,添加!!! [C_]: https://blog.baiguiren.com/2020/04/22/cpp/cpp%E6%A8%A1%E7%89%88/%E5%B0%86CPP%E6%A8%A1%E6%9D%BF%E5%BA%94%E7%94%A8%E4%BA%8E%E5%A4%9A%E6%96%87%E4%BB%B6%E7%BC%96%E7%A8%8B/
相关 C++ 模板 C++入门系列文章: [C++、STL常用容器][C_STL] [C++、STL – 函数对象、常用算法][C_STL _] 前言: > 学习模板并不是为了写模板,而 冷不防/ 2024年03月25日 12:45/ 0 赞/ 78 阅读
相关 C++模板 C++模板 1. 模板概念 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板的原理 客官°小女子只卖身不卖艺/ 2023年09月28日 12:09/ 0 赞/ 20 阅读
相关 C++模板 1. 为什么要使用模板? 1. 假如设计一个求两参数最大值的函数,在实践中我们可能需要定义四个函数: ![format_png][] 2. 这些函数 怼烎@/ 2023年05月31日 06:56/ 0 赞/ 17 阅读
相关 C++ 模板: 函数模板 文章目录 C++ 模板 函数模板 1. 模板的概念 2. 函数模板 2.1 函数模板语法 2.2 秒速五厘米/ 2022年12月30日 12:55/ 0 赞/ 274 阅读
相关 C++模板 函数模板 函数模板,是可以创建一个通用的函数,可以支持多种形参。 用关键字 `template` 来定义, 在函数模板中,数据的值和类型都被参数化了,发生函数调用时编 你的名字/ 2022年12月23日 00:43/ 0 赞/ 126 阅读
相关 C++模板 C++模板 ①模板是实现代码重用机制的一种工具。就是根据参数类型生成函数和类的机制。它可以分成两类:一是函数模板,二是类模板,他们允许用户构造模板函数,模板类。 也可称通用 谁借莪1个温暖的怀抱¢/ 2022年09月17日 11:20/ 0 赞/ 149 阅读
相关 c++模板 1定义函数模板 include<stdexcept> include <sstream> include <map> using namesp 水深无声/ 2022年08月21日 08:55/ 0 赞/ 167 阅读
相关 C++:模板 http://[blog.csdn.net/pipisorry/article/details/72353250][blog.csdn.net_pipisorry_articl 逃离我推掉我的手/ 2022年06月16日 13:59/ 0 赞/ 199 阅读
相关 c++模板 1.类模板及其(全)特化和偏特化 模板特化是通过"给模板中的所有模板参数一个具体的类"的方式来实现的.而模板偏特化则是通过"给模板中的部分模板参数以具体的类,而留下剩余的模板 红太狼/ 2022年05月17日 03:49/ 0 赞/ 160 阅读
相关 C++模板 文一:[/images/20220319/2dfa5cca396940129714244edcacafad.png][http_www.cnblogs.com_CaiNiaoZ Bertha 。/ 2022年03月19日 13:30/ 0 赞/ 243 阅读
还没有评论,来说两句吧...