当前位置: 首页 > news >正文

C++之模板进阶(探索C++模板:非类型参数与特化技巧)

本节目标:

1.非类型模板参数

2.类模板的特化

3.类模板特化的应用之类型萃取

4.模板的分离编译

非类型模板参数

模板参数分    类型形参与非类型形参

类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称

非类型形参:就是用一个常量作为类(函数)模板的一个参数,可将参数当成常量来使用

比如

template<class T>
class A
{bool operator()(const T&x)
{}}

这种是类型模板参数,因为你创建A的时候要指明类型,比如 A<int> a;   int就是一个类型

那什么叫非类型模板参数??? 

template<int N>

这种就是非类型模板参数,直接定义了一个N,作用就类似#define N 100;

这样你传参就可以 A<100> a;     // 这个100就会传给N

非类型模板参数的作用(解决什么应用场景)

 

此时我的数组长度已经固定为100,那如果我想弄一个200的呢???

就是我封装了一个静态数组,无非改变其容量

那非类型模板参数就可以解决这类问题

 

注意事项: 

1.注意这个N是不能修改的,是一个常量

2..浮点数,类对象以及字符串是不允许作为非类型模板参数的

3.非类型的模板参数必须在编译期就能确定结果

class MyClass { /* ... */ };MyClass obj;  // 运行时创建的对象template <MyClass obj>  // 错误!无法在编译时确定obj的值
class A { /* ... */ };

字符串常量是一个指针,指针指向的物理地址是会变的

浮点数主要与精度有问题,每个编译器处理不一样

模板的特化 

什么叫模板的特化???

模板的特化就是模板的特殊化,通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,也就是你想针对这个类型的模板特殊化处理得到一些特殊的结果

// 通用模板
template <typename T>
struct Comparator {bool operator()(const T& a, const T& b) const {return a == b;  // 默认使用 ==运算符}
};// 特化版本:针对 const char*(C风格字符串)
template <>
struct Comparator<const char*> {bool operator()(const char* a, const char* b) const {return std::strcmp(a, b) == 0;  // 使用strcmp进行字符串比较}
};

如果没有特化版本

那我们在调用Comparator("hello","world")去比较两个字符串的大小的时候

就会直接调通用版本,你本质是想比较hello和world,但你传的是一个字符串常量

也就是你的类型是const char*,指针,比较的是指针,这与你原来的意思相背

所以此时就需要模板的特化 

针对某些类型的特殊化处理,与正常模板处理不一样

模板的特化分为函数模板特化和类模板特化

函数的模板特化:

1.必须要先有一个基础的函数模板

2.关键字template后面接一对空的尖括号<>

3.函数名后跟一对尖括号,尖括号中指定需要特化的类型

4.函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的    错误

// 通用模板
template <typename T>
ReturnType functionName(T param) {// 通用实现
}// 全特化版本(针对特定类型)
template <>
ReturnType functionName<SpecificType>(SpecificType param) {// 特化实现
}

注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出 (就是你直接自己实现一个就行了,不要去想什么特不特化)

总结:模板特化必须遵循 “先通用,后特化” 的顺序。这是 C++ 模板系统的设计原则,确保类型安全和编译期的正确匹配。如果需要为特定类型提供完全不同的实现,且不想依赖通用模板,建议使用函数重载独立类替代特化。

类模板的特化

对于类模板的特化 :分为全特化和偏特化

全特化:将模板参数列表中所有的参数都确定化

偏特化:任何针对模板参数进一步进行条件限制设计的特化版本

// 通用模板
template <typename T>
class Container {
public:void print() { std::cout << "General Container" << std::endl; }
};// 全特化:针对int类型
template <>
class Container<int> {
public:void print() { std::cout << "Int Container" << std::endl; }
};

这是一个全特化,这个和函数模板特化类似,template后面跟一对<>,并且在类名后面<int>

在 C++ 中,偏特化(Partial Specialization)的语法 要求在 template 关键字后的尖括号中保留未被特化的参数,并在类名后的尖括号中指定特化的部分

偏特化有以下两种表现方式:

部分特化:将模板参数列表中的一部分参数特化 

// 通用模板:两个参数
template <typename T, typename U>
class Pair {
public:void print() { std::cout << "General Pair" << std::endl; }
};// 部分特化:固定第二个参数为int
template <typename T>
class Pair<T, int> {
public:void print() { std::cout << "Pair with int: " << typeid(T).name() << std::endl; }
};

参数更进一步的限制

template <typename T>
class Container {
public:void process() { std::cout << "General Container" << std::endl; }
};// 参数限制:针对指针类型
template <typename T>
class Container<T*> {
public:void process() { std::cout << "Pointer Container: " << typeid(T).name() << std::endl; }
};// 参数限制:针对引用类型
template <typename T>
class Container<T&> {
public:void process() { std::cout << "Reference Container" << std::endl; }
};

总结

  • 类模板:全特化和偏特化均支持,用于定制类的行为。
  • 函数模板:仅支持全特化,偏特化需通过函数重载替代。
  • 最佳实践:优先使用函数重载处理不同参数类型,仅在必要时使用类模板特化。
  • 偏特化:偏特化就是对参数的进一步限制

模板分离编译 

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

为什么要分离编译?
方便维护和查看,直接.h头文件看函数功能,.cpp源文件看功能实现

对于普通的函数和类,我们可以进行分离编译,但函数模板和类模板不行???

我们首先回顾一个源文件如何生成目标文件

 

对于这个过程不熟悉的可以自行查看,或者看我的函数名重载那一篇文章 

Fun.h//模板函数的声明
Fun.cpp//这里面包含模板函数
test.cpp//包头文件

头文件Fun.h 

template<class T>
T add(const T&left,const T&right);

 源文件Fun.cpp

template<class T>
T add(const T&left,const T&right)
{return left+right;
}

test.cpp

#include "Fun.h"
int main()
{add(1,2);add(1.0,2.0);
return 0;
}

 包头文件就相当于一种承诺,会检查是否符合头文件声明函数的参数,返回类型等

但源文件是单独编译的,也就是Fun.cpp和test.cpp是单独编译的

Fun.cpp因为没有实例化的参数,所以没有实例化版本,不会生成具体的加法函数

test.cpp编译时有实例化参数,但没有函数,当它编译好,要去链接,发现没有函数地址,找不到

所以就会报链接错误

如何解决???

1.将声明和定义放到一个文件中,也就是Fun.cpp中的放入Fun.h,不要模板分离编译

这样包头文件时,就会包到test.cpp中

2.模板定义的位置显示实例化。这种方法不实用,不推荐使用

也就是直接在Fun.cpp中直接add<int>指定生成什么,这看着就很挫,你是知道test.cpp中使用什么才实例化,但在实际中是不知道的,所以这种方法不推荐

3.export关键字

大多数编译器不支持,实用性差

模板总结

优点:

1.模板复用了代码,节省了资源,更快的迭代开发,c++的标准模板库(STL)因此而产生

2.增强了代码的灵活性

缺点:

1.模板会导致代码膨胀问题,也会导致编译时间变长

2.出现模板编译错误时,错误信息非常凌乱,不易定位错误

 

 

 

 

 

相关文章:

  • 数据结构与算法——堆
  • 【人工智能发展史】从黎明到曙光02
  • 汽车转向系统行业2025数据分析报告
  • OpenCv高阶(六)——指纹识别
  • 车辆诊断技术全生命周期管理与实践
  • PDF 文档结构化工具对比:Marker 与 MinerU
  • 深入解析Dify:从架构到应用的全面探索
  • 国产linux系统(银河麒麟,统信uos)使用 PageOffice实现PDF文件加盖印章和签字功能
  • Node.js多版本安装工具NVM详细使用教程
  • 网络世界的“变色龙“:动态IP如何重构你的数据旅程?
  • 智防火灾,慧控能耗:物联网赋能金融行业电气安全革新
  • [java]数组
  • 【工具】grcMalaria用于处理和分析“斑点疟疾基因报告卡”的R软件包
  • 工商总局可视化模版-Echarts的纯HTML源码
  • RestTemplate 发送的字段第二个大写字母变成小写的问题探究
  • 数据被泄露了怎么办?
  • 机器学习中的泛化能力
  • PostgreSQL中通过查询数据插入到表的几种方法( SELECT INTO和INSERT INTO ... SELECT)
  • 离线服务器Python环境配置指南
  • 微信小程序中,解决lottie动画在真机不显示的问题
  • 焦作建网站/东莞排名优化团队
  • 微官网和公众号的区别/奇零seo赚钱培训
  • 网站建设放入什么会计科目/站长网站大全
  • wordpress速度加快/宁波优化网站哪家好
  • 华夏网站建设/app联盟推广平台
  • 寿阳网站建设/郑州网站建设制作