【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++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
127 10
|
5月前
|
编译器 C++
【C++】——初识模板
【C++】——初识模板
【C++】——初识模板
|
6月前
|
程序员 C++
C++模板元编程入门
【7月更文挑战第9天】C++模板元编程是一项强大而复杂的技术,它允许程序员在编译时进行复杂的计算和操作,从而提高了程序的性能和灵活性。然而,模板元编程的复杂性和抽象性也使其难以掌握和应用。通过本文的介绍,希望能够帮助你初步了解C++模板元编程的基本概念和技术要点,为进一步深入学习和应用打下坚实的基础。在实际开发中,合理运用模板元编程技术,可以极大地提升程序的性能和可维护性。
|
2月前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
66 4
|
2月前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
37 3
|
2月前
|
编译器 C++
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
34 0
|
3月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
24 1
|
3月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
55 9
|
3月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
75 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
3月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
101 2