【C++】Template:深入理解特化与分离编译,破解编译难题

GPU架构(1.1)--介绍 GPU的 VPU(Vision Processing Unit)架构 基于自研的 VPU(Vision Processing Unit)架构,专为高性能图形处理和深度学习设计。VPU 架构通过优化计算单元、内存层次结构、同步机制和指令流水线,提供了卓越的性能和能效。VPU 架构通过优化计算单元、内存层次结构、同步机制和指令流水线,提供了卓越的性能和能效。这种架构不仅适用于高性能图形处理,还广泛应用于深度学习、科学计算和边缘计算等领域。通过了解 VPU 架构的详细设计和工作原理,读者可以更好地利用这些先进的 GPU 技术,开发出高效、可靠的计算系统。 阅读详情

​

🔥小陈又菜:个人主页

📖个人专栏:《MySQL:菜鸟教程》《小陈的C++之旅》《Java基础》

✨️想多了都是问题,做多了都是答案!


​


目录

1. 数组模板实例和非类型模板参数

表达式参数的限制:

表达式参数的优点:

表达式参数的缺点:

2. 模板的特化

2.1. 什么是模板特化?

2.2. 函数模板特化

2.3. 类模板特化

2.3.1. 全特化

2.3.2. 偏特化

3. 模板分离编译

3.1. 什么是分离编译

3.2. 模板的分离编译

4. 模板总结


本篇内容主要包括非典型模板参数、类模板的特化、模板的分离编译。对于C++模板还不是很了解的小伙伴可以先看一下这篇文章:《C++模板:让你的代码更通用更优雅》

1. 数组模板实例和非类型模板参数

模板经常作为容器类,因为类型参数的概念就很适合将相同的储存方案用于不同种数据类型。

这一通过一个简单的,能够指定大小的数组模板来进行说明。有两种方式来实现这个数组模板:

  • 在模板类中使用动态数组,通过构造函数传入参数来提供元素数量
  • 通过模板参数来提供元素数量大小(数组大小)

C++新增的array采用的就是第二种方法:

// 定义一个模板类型的静态数组
template<class T, size_t N = 10>
class array
{
public:
	T& operator[](size_t index) { return _array[index]; }
	const T& operator[](size_t index)const { return _array[index]; }
 
	size_t size()const { return _size; }
	bool empty()const { return 0 == _size; }
 
private:
	T _array[N];
	size_t _size;
};

我们可以看到上面的代码中,class关键字(或者是等价的typename)指定T是类型参数,但是后面的size_t指定N是一个无符号整数。这种参数(指定了数据类型而不是使用泛型名称)就叫做非类型参数(nontype)或者是表达式参数。

表达式参数的限制:

  • 可以是整型,枚举,引用或指针(所以Double是不被允许的,但是Double*是被允许的)
  • 模板代码不能修改表达式参数的值,也不能使用它的地址(所以不能出现N++、&N等)
  • 在实例化参数模板时,作为表达式参数的值必须是常量参数

表达式参数的优点:

与前面提到的第一中使用构造函数相比,构造函数使用的是new和delete来管理堆内存,这种方法操作消耗大、容易造成内存碎片化、内存泄漏。与之相比,表达式参数的方法使用的是栈内存,这种方法无疑更快,尤其是在处理大量小型数组的时候。

表达式参数的缺点:

使用表达式参数,当模板实例化的时候会为每一个不同的N生成一个类(一份代码):

template<class T, size_t N>
class Array {
    T data[N];
};

// 不同的N值会产生不同的类
Array<int, 5> arr5;    // 生成 Array<int, 5> 类
Array<int, 10> arr10;  // 生成 Array<int, 10> 类

// 编译器实际生成的代码:
class Array_int_5 {    // Array<int, 5> 的特化
    int data[5];
};

class Array_int_10 {   // Array<int, 10> 的特化  
    int data[10];
};

这样的后果就是:

  • 代码膨胀
  • 编译时间增加
  • 二进制文件体积增大

而相比之下,使用构造函数的方法(动态数组)就显得更加灵活。

2. 模板的特化

2.1. 什么是模板特化?

我们知道,模板的使用就是为了代码的复用率更高,也就是说写一些与数据类型无关的代码,单是不能避免的是,对于一些特殊的类型就可能会得到一下错误的结果,我们通过一个例子来进行说明:

template<class T>
bool Less(T left, T right)
{
	return left < right;
}
 
int main()
{
	cout << Less(1, 2) << endl;//可以比较
 
	return 0;
}

运行结果:

除了内置类型,自定义类型也是可以通过模板来进行比较,例如我们之前实现的Date日期类:

但是我们来看一下下面这段代码还能够输出正确的结果吗?

Date d1(2025, 9, 21);
Date d2(2025, 9, 20);

Date* p1 = &d1;
Date* p2 = &d2;

cout << Less(p1, p2) << endl;

很显然它返回了一个错误的结果,因为p1指向的对象d1明显大于p2指向的对象d2,但是却依旧返回了true,这是因为传入的是p1和p2,此时比较的是两者的指针地址。

那么模板特化就有了用武之地,模板特化就是在原有的模板基础上,针对特殊的类型进行特殊化的实现方式,可分为函数模板特化、类模板特化。

2.2. 函数模板特化

函数模板特化过程:

  • 必须先有一个基础模板(没有基础模板也就没有特化的对象)
  • template后面跟一个空的<>(尖括号)
template<>  // 空的尖括号表示这是特化版本
void print<int>(int value) {
    cout << "int特化版本: " << value << endl;
}
  • 函数名后面跟一个<>尖括号指定特化的类型
void print<int>(int value) {  // <int> 指定特化类型
    // 实现针对int的特化逻辑
}
  • 函数形参表必须完全相同(如果不同编译可能会报错)
// 基础模板
template<class T>
void print(T value);  // 参数:T value

// 正确特化 - 参数类型匹配
template<>
void print<int>(int value);  // 参数:int value ✅

// 错误特化 - 参数类型不匹配
template<>
void print<int>(double value);  // 错误!❌

我们还是那刚才的Date日期类做一个例子:

template<class T>
bool Less(T left, T right)
{
	return left < right;
}
 
//日期类函数模板特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}
int main()
{
	cout << Less(1, 2) << endl;//可以比较
 
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl;
 
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl;
	return 0;
}

这次我们就得到了正确的结果:

2.3. 类模板特化

2.3.1. 全特化

下面同样以Date类做一个举例:

////全特化
template<>
class Data<int, double>
{
public:
	Data()
	{
		cout << "Date<int,double>"<<endl;
	}
};

我们发现在全特化同样是,

template<>

这里要注意的几个容易混淆的点:

  1. 将类模板全特化之后,这个全特化的版本并不是类模板
  2. 并不是说类模板就不能接收int、double两种不同类型的参数,而是说我们在接收两种不同类型参数之后,我们希望有不同于原类模板的成员函数

通过全特化我就能在传入特定参数类型时,去特化函数中的相应的处理:

2.3.2. 偏特化

偏特化:是对类模板参数的进一步限制

可以将其分为两种形式:

  • 将模板类型参数列表中的参数部分特化
  • 将参数进行更进一步的限制

部分特化:

////半特化  第二个参数总是int类型
////1、将模板参数类表中的一部分参数特化。
template<class T1>
class Data<T1,int>
{
public:
	Data()
	{
		cout << "Date<T1,int>"<<endl;
	}
};

针对参数做进一步特化:

将参数偏特化为指针类型

//偏特化 参数给定了 两个指针类型 具体那种不确定
//2、偏特化并不仅仅是指特化部分参数,
//   而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
template<class T1, class T2>
class Data<T1*, T2*>
{
public:
	Data()
	{
		cout << "Data<T1*,T2*>" << endl;
	}
};

这个地方注意特化优先级的一个问题:

优先级 :全特化 > 半特化 > 默认

3. 模板分离编译

3.1. 什么是分离编译

程序是由许多个源文件组成的,编译器会将这多个源文件编译成为目标文件,然后由链接器形成单一的可执行文件,这个过程就叫做分离编译。

3.2. 模板的分离编译

在大多数情况下,模板的声明和定义是分开的,在头文件中声明,在源文件中定义:

// a.h
template<class T> 
T Add(const T& left, const T& right);
// a.cpp
template<class T> T Add(const T& left, const T& right) {
	return left + right;
}
// main.cpp
 
#include"a.h"
 
int main()
{
	Add(1, 2);
	Add(1.0, 2.0);
 
	return 0;
}

那么接下来我们来看一看根据这个步骤:预处理-->编译-->汇编-->链接 会有一些什么问题:

也就是说因为模板只有在传入了实际的类型参数之后才会生成实际的代码,那么在链接那一步的时候就会出现错误。

解决方式:

1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者 xxx.h 其实也是可以的 。推荐使用这种。

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

4. 模板总结

  • 优点:

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

        2.增强了代码的灵活性,简化编程工作,提高程序的可靠性。

  • 缺点:

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

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


(本篇完)

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况 主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。 立即下载

相关推荐

6层高速PCB设计实战:BGA布局与阻抗匹配关键解析

本文深入解析6层高速PCB设计中BGA布局与阻抗匹配的关键技术。通过实战经验分享,详细介绍了六层PCB层叠结构的选择与优化、BGA器件布局技巧、阻抗匹配的实战经验以及高速布线技巧,帮助工程师提升PCB设计水平,确保信号完整性和电源完整性。

weixin_28481133的博客 368

template之全特化和偏特化

前言 关于讲过traits萃取器的时候探讨到偏特化的概念, 而在那一篇文章也没有具体解释偏特化是什么, 怎么实现, 所以可能在第一次看得时候会很莫名其妙. 所以我将偏特化放在其后讲解, 为不明白的朋友做一个浅析的讲解. 这里我先聊一下全特化再聊偏特化. 全特化 全特化的模板参数列表应该是为空, 函数和类都可以实现全特化. template&amp;amp;lt;class T&amp;amp;gt; void fun(T a) ...

啦啦啦 2318

从ResNet到EfficientNet:特征金字塔网络(FPN)在不同骨干网中的适配指南

本文深入探讨了特征金字塔网络(FPN)与ResNet、VGG、EfficientNet等主流骨干网络的适配策略。文章详细解析了FPN的核心原理,并针对不同骨干网络的特征提取特性,提供了通道对齐、参数量估算及迁移学习的系统化指南,旨在帮助开发者在目标检测等视觉任务中构建高性能、高效率的模型。

weixin_29047919的博客 220

C++模板特化与偏特化 template <>的含义

C++模板特化与偏特化

ULTRAmanTAROACE的博客 840

本地AI代码助手:智能解析大型代码库,实现自动化重构与理解

在软件开发中,大型代码库的理解与维护是常见挑战。传统方法依赖人工阅读和有限上下文工具,效率低下。通过结合检索增强生成(RAG)和多智能体(Multi-Agent)架构,现代AI工具能够智能解析代码结构,理解模块关系,并精准定位相关代码片段。这种技术不仅能提升代码探索效率,还能自动化执行重构、文档生成等任务,在遗留系统维护、快速上手新项目等场景中发挥关键作用。本文以blackboxaicode/cli为例,展示了如何利用其代码感知和Workflow Automation能力,高效处理复杂代码库。

weixin_30240349的博客 363

如何处理MIGO取消物料凭证报错:“只是可能对有相同的价格控制进行倒记账(本月S,前月V)” & “仅在 QM 中修改物料质量检验库存”

记录一次处理MM模块的报错。这次连续处理了两个错误,“只是可能对有相同的价格控制进行倒记账(本月S,前月V)” & “仅在 QM 中修改物料质量检验库存”。把过程和操作步骤记录下一。方便日后回头查找。

IT小职员 6232

C++ 模板特化与偏特化

文章目录1.模板特化1.1 概述1.2 函数模板特化1.3 类模板特化2.模板偏特化2.1 概述2.2 函数模板偏特化2.3 类模板偏特化3.模板类调用优先级参考文献 1.模板特化 1.1 概述 模板特化(template specialization)不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板特化。模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。 1.2 函数模板特化 函数模板特化指函数模板在模板参数为特定类型下的特定实现。查看以下示例: #include <i

Doublelv 的博客专栏。 2万+

iqoo11如何获取root权限?

🏆本文收录于专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家!持续更新中,up!up!up!!

**My Coding Family** 5816

[ C++ ] template 模板进阶 (特化,分离编译)

本篇内容包括C++ 非典型模板参数,类模板的特化,模板的分离编译。

优快云博客 2221

YOLOv8改进策略【YOLO和Mamba】| 2024 VM-UNet,高效的特征提取模块VSS block 二次创新提高精度

VM-UNet模型设计的出发点是解决现有CNN-based和模型在医学图像分割中的局限性,利用(SSMs)的优势,提出一种更有效的医学图像分割模型。具体来说,CNN-based模型在捕捉长距离信息方面存在不足,而模型由于自注意力机制的二次复杂度导致计算负担较重。SSMs模型如Mamba不仅在建模长距离依赖方面表现出色,还具有线性计算复杂度,这为VM-UNet的设计提供了理论基础。

Limiiiing的博客 616

华为OD机考双机位C卷 - 虚拟理财游戏(Java & Python& JS & C/C++ & GO )

华为OD机考双机位C卷 - 虚拟理财游戏(OD上机考试双机位C卷)。提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。

算法大师! 668

模版的偏特化

模版的特化与偏特化 模版的特化与偏特化 Partial Template Specialization能够让你在模板(Template)的所有可能的实体中特化出一组子集. 1.模板的特化(template specialization): 例如,定义如下的一个模板: template class Widget { ...

daojin505的专栏 298

Linux 配置 multipath 多路径

每天一个 DBA 小知识,助你更进一步!

LuciferLiu_DBA 3297
上一篇: 最简单的方式做最系统的教学【计算机组成入门到入土】解码计算机:冯·诺依曼架构如何主宰你的每一次点击
下一篇: 【C++】别再用错public了!C++继承暗坑全图鉴,虚继承才是救世主
小陈又菜
博客等级 码龄3年 680粉丝 102原创
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值