【C++】-- 模板进阶(二)

简介: 【C++】-- 模板进阶

(2)偏特化

偏特化是任何针对模板参数进一步进行条件限制的特化版本。偏特化分为两种表现方式:

①部分特化:把模板参数类表中的一部分参数特化

1. //偏特化-部分特化,把第二个参数特化为double
2. template<class T1>
3. class Data<T1, double>
4. {
5. public:
6.  Data()
7.  {
8.    cout << "Data<T1,T2>" << endl;
9.  }
10. private:
11.   T1 _d1;
12.   double _d2;
13. };

这时候d2就会去调这个偏特化 :

②对参数进一步限制:对模板参数做更进一步的条件限制

可以将参数偏特化为指针类型 :

1. //偏特化-两个参数偏特化为指针类型,只指定指针,什么类型的指针都可以
2. template<typename T1,typename T2>
3. class Data<T1*, T2*>
4. {
5. public:
6.  Data()
7.  {
8.    cout << "Data<T1*,T2*>" << endl;
9.  }
10. private:
11.   T1 _d1;
12.   T2 _d2;
13. };
1. void Test_Class()
2. {
3.  Data<int, int> d1;
4.  Data<int, double> d2;
5.  Data<int, char> d3;
6.  Data<char*, char*> d4;//会调用两个参数偏特化为指针类型的偏特化
7. }

也可以将参数偏特化为引用类型:

1. //偏特化-两个参数偏特化为引用类型,只指定引用,什么类型的引用都可以
2. template<typename T1, typename T2>
3. class Data<T1&, T2&>
4. {
5. public:
6.  Data(const T1& d1,const T2& d2)
7.    :_d1(d1)
8.    ,_d2(d2)
9.  {
10.     cout << "Data<T1&,T2&>" << endl;
11.   }
12. private:
13.   const T1& _d1;
14.   const T2& _d2;
15. };
1. void Test_Class()
2. {
3.  Data<int, int> d1;
4.  Data<int, double> d2;
5.  Data<int, char> d3;
6.  Data<char*, char*> d4;//会调用两个参数偏特化为指针类型的偏特化
7.  Data<double&, double&> d5(2.1, 3.2);//会调用两个参数偏特化为引用类型的偏特化
8. }

最后,编译器匹配参数时,会自动匹配最匹配的那个,编译器实例化之后就没有模板了,实例化之后就会把T1&替换成double&,把T2&替换成double&,就变成了普通类。

假如参数类型一个是指针,一个是引用呢?将一个参指定为指针类型,另外一个参数指定为引用类型

1. //偏特化-一个参数偏特化为指针类型,一个参数偏特化为引用类型
2. template<typename T1, typename T2>
3. class Data<T1*, T2&>
4. {
5. public:
6.  Data()
7.  {
8.    cout << "Data<T1*,T2&>" << endl;
9.  }
10. };
Data<int*, double&> d6;//会调用一个参数偏特化为指针类型,一个参数偏特化为引用类型的偏特化

三、模板分离编译

分离式编译:一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,将所有目标文件连接起来形成单一的可执行文件的过程称为分离编译模式。

1.模板支持分离编译吗

对于分离式编译的模板:

template.h

1. template<class T>
2. T Add(const T& left, const T& right);

template.cpp

1. #include "template.h"
2. 
3. template<class T>
4. T Add(const T& left, const T& right)
5. {
6.  return left + right;
7. }

template-main.cpp  

1. #include "template.h"
2. 
3. int main()
4. {
5.  Add(1, 2);
6.  Add(1.0, 2.0);
7. 
8.  return 0;
9. }

上述代码运会报错:

2.为什么模板不支持分离编译

程序要运行,需要经过预处理、编译、汇编、链接4个阶段

(1)预处理

包含头文件、宏替换、、条件编译、删除注释

(2)编译

语法分析、词法分析、语义分析,符号汇总

(3)汇编

汇编代码转换成机器指令 、生成符号表

(4)链接

链接目标文件和连接库、合并符号表、重定位

预处理阶段,#include "template.h"包含头文件,认为会有Add函数的定义。编译时在符号表里填上符号和地址,生成符号表,编译阶段只有函数声明,没有函数定义,如果有人调用了这个符号,就能通过符号表找到这个符号对应的地址。但是函数模板不会生成对应符号。汇编时把符号转换成二进制,这样,机器就可以识别了。链接时会去找函数地址进行合并重定位。

虽然编译时,想生成函数模板对应的符号,但是此时并不知道T是什么,由于在链接之前,各个文件不知道对方的存在,所以template-main.cpp无法告诉template.cpp T具体是什么,template.cpp中无法对T进行实例化,会寄希望于最后一步链接,所以不会生成对应符号。即,定义的地方(template.cpp)不实例化,而实例化的地方(template-main.cpp)没有定义,只有声明。没有实例化T,生成不了函数模板的符号。

3.如何编译模板文件

如何解决函数模板不能生成对应符号,导致程序运行不了的问题呢?有两种方法

(1)显式指定实例化

模板定义时直接指定T的类型,即增加模板的特化,将template.cpp改为:

1. #include "template.h"
2. 
3. template<class T>
4. T Add(const T& left, const T& right)
5. {
6.  return left + right;
7. }
8. 
9. //增加int类型的模板特化
10. template<>
11. int Add<int>(const int& left, const int& right)
12. {
13.   return left + right;
14. }
15. 
16. //增加double类型的模板特化
17. template<>
18. double Add<double>(const double& left, const double& right)
19. {
20.   return left + right;
21. }

(2)将声明和定义放到一个文件中(推荐)

将template.h和template.cpp合并成一个文件,这个文件名可以是template.h,也可以是template.cpp,此时template就只剩下一个文件了。

template.h

1. template<class T>
2. T Add(const T& left, const T& right);
3. 
4. template<class T>
5. T Add(const T& left, const T& right)
6. {
7.  return left + right;
8. }

template.main不变

1. #include "template.h"
2. 
3. int main()
4. {
5.  Add(1, 2);
6.  Add(1.0, 2.0);
7. 
8.  return 0;
9. }

编译通过。

相关文章
|
3月前
|
存储 算法 安全
c++模板进阶操作——非类型模板参数、模板的特化以及模板的分离编译
在 C++ 中,仿函数(Functor)是指重载了函数调用运算符()的对象。仿函数可以像普通函数一样被调用,但它们实际上是对象,可以携带状态并具有更多功能。与普通函数相比,仿函数具有更强的灵活性和可扩展性。仿函数通常通过定义一个包含operator()的类来实现。public:// 重载函数调用运算符Add add;// 创建 Add 类的对象// 使用仿函数return 0;
122 0
|
3月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
92 0
|
11月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
238 10
|
编译器 C++
【C++】——初识模板
【C++】——初识模板
【C++】——初识模板
|
6月前
|
编译器 C++
模板(C++)
本内容主要讲解了C++中的函数模板与类模板。函数模板是一个与类型无关的函数家族,使用时根据实参类型生成特定版本,其定义可用`typename`或`class`作为关键字。函数模板实例化分为隐式和显式,前者由编译器推导类型,后者手动指定类型。同时,非模板函数优先于同名模板函数调用,且模板函数不支持自动类型转换。类模板则通过在类名后加`&lt;&gt;`指定类型实例化,生成具体类。最后,语录鼓励大家继续努力,技术不断进步!
|
7月前
|
编译器 C++
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
|
7月前
|
安全 C++
【c++】模板详解(2)
本文深入探讨了C++模板的高级特性,包括非类型模板参数、模板特化和模板分离编译。通过具体代码示例,详细讲解了非类型参数的应用场景及其限制,函数模板和类模板的特化方式,以及分离编译时可能出现的链接错误及解决方案。最后总结了模板的优点如提高代码复用性和类型安全,以及缺点如增加编译时间和代码复杂度。通过本文的学习,读者可以进一步加深对C++模板的理解并灵活应用于实际编程中。
100 0
|
7月前
|
存储 安全 算法
深入理解C++模板编程:从基础到进阶
在C++编程中,模板是实现泛型编程的关键工具。模板使得代码能够适用于不同的数据类型,极大地提升了代码复用性、灵活性和可维护性。本文将深入探讨模板编程的基础知识,包括函数模板和类模板的定义、使用、以及它们的实例化和匹配规则。
|
10月前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
332 4
|
10月前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
133 3