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

《C++初阶之STL》【模板参数 + 模板特化 + 分离编译】

【模板参数 + 模板特化 + 分离编译】

  • 前言:
  • ------------模板参数------------
    • C++的模板参数有哪些?
      • 一、类型参数
      • 二、非类型参数
      • 三、模板模板参数
  • ------------模板特化------------
  • 1. 什么是模板特化?
  • 2. 为什么要使用模板特化?
  • 3. 模板特化有哪些?
    • 一、函数模板特化
      • 函数模板特化的步骤
      • 函数模板全特化
      • 函数模板偏特化
    • 二、类模板特化
      • 类模板全特化
      • 类模板偏特化
  • ------------分离编译------------
    • 什么是分离编译?
    • 模板的分离编译要注意什么事情?
    • 怎么解决模板分离编译时带来的问题?

在这里插入图片描述

往期《C++初阶》回顾:

/------------ 入门基础 ------------/
【C++的前世今生】
【命名空间 + 输入&输出 + 缺省参数 + 函数重载】
【普通引用 + 常量引用 + 内联函数 + nullptr】
/------------ 类和对象 ------------/
【类 + 类域 + 访问限定符 + 对象的大小 + this指针】
【类的六大默认成员函数】
【初始化列表 + 自定义类型转换 + static成员】
【友元 + 内部类 + 匿名对象】
【经典案例:日期类】
/------------ 内存管理 ------------/
【内存分布 + operator new/delete + 定位new】
/------------ STL ------------/
【泛型编程 + STL简介】
【auto关键字 + 范围for循环 + 迭代器】
【string类:详解 + 实现】
【vector容器:详解 + 实现】
【list容器:详解 + 实现】
【stack/queue/priority_queue容器适配器:详解 + 实现】

前言:

hi~ 小伙伴们大家好呀!(≧∇≦)ノ 后天就是立秋了,夏天即将结束,但炎热的天气还没过去,可能还要持续两周左右呢~(>_<)

随着夏天的结束,《C++ 初阶》也迎来了最后一篇博客:【模板参数 + 模板特化 + 分离编译】 ✨。(๑¯∀¯๑))~这时可能会有小伙伴吐槽:看博主之前的博客,全是 “C 语言、算法、数据结构、C++” 初阶的这类内容,啥时候才能学到进阶的知识呀,哈哈~( ̄▽ ̄)ゞ 让大家久等了,但是这次关于C++的故事还并没结束,下期《C++ 进阶》敬请期待哦!(ノ◕ヮ◕)ノ:・゚✧

------------模板参数------------

C++的模板参数有哪些?

模板(Template):是泛型编程的核心机制,允许在编写代码时使用参数化的类型,从而实现代码的复用。

模板的参数分为两大类类型参数非类型参数,此外还有 模板模板参数(较少见)

  • :博主可没有打错字哦~,名字真的就叫作模板模板参数啊!

一、类型参数

类型参数(Type Parameters):表示模板中使用的 数据类型

  • 可以是 内置类型intdouble
  • 或者是 自定义类型:类、结构体)

1. 通用类型参数

  • 使用class或typename声明,两者含义相同(推荐用typename,更清晰)

    template <class T>        // 类型参数 T
    void swap(T& a, T& b) 
    {T temp = a;a = b;b = temp;
    }template <typename U>    // 等价于 class U
    class Vector { /* ... */ };
    
  • 示例调用:

    swap<int>(3, 5);          // 显式指定类型为 int
    swap<double>(3.14, 2.71); // 显式指定类型为 double// 也可隐式推导类型:swap(3, 5);(编译器自动推导为 int)
    

2. 默认类型参数

  • 可以为类型参数指定默认类型,调用时若未指定则使用默认值

    template <typename T = int> // 默认类型为 int
    class Stack 
    {// ...
    };Stack<> s;       // 使用默认类型 int
    Stack<double> d; // 显式指定类型为 double
    

二、非类型参数

非类型参数(Non-Type Parameters):表示模板中使用的 常量值

  • 通常为整型枚举值指针/引用
  • C++11 后支持 std::nullptr_tconstexpr 变量等

1. 基本语法

template <typename T, int Size> // T 是类型参数,Size 是非类型参数(整数)
class Array 
{
private:T data[Size]; // 使用非类型参数 Size 作为数组长度public:int getSize() const { return Size; }
};

2. 限制条件

  • 非类型参数必须是编译期可确定的常量,不能是变量或运行时计算的值

    Array<int, 10> arr;   // 合法,10 是编译期常量int n = 10;
    // Array<int, n> arr; // 非法,n 是变量,非编译期常量
    
  • 对于指针/引用类型的非类型参数,要求其指向的对象具有静态存储期(如:全局变量static变量

    int global_var = 0;template <int* ptr>
    void func() { /* ... */ }func<&global_var>(); // 合法,指向全局变量
    

代码案例:非类型参数的使用小案例

namespace mySpace
{//任务:定义的一个“静态数组”的模板类,同时要使用“非类型参数”template<class T, size_t N = 10>class array{private:T _array[N];  //1.存储数据的静态数组   注意:在编译期确定数组大小size_t _size; //2.记录数组中有效元素数量的变量public://1.实现:“普通版本的下标运算符[]的重载函数”T& operator[](size_t index)  //注意:支持对数组元素的读写操作{return _array[index];}//2.实现:“const版本的下标运算符[]的重载函数”const T& operator[](size_t index)const  //注意:保证在只读场景下也能通过下标访问元素,返回的是 const 引用,确保元素不会被修改{return _array[index];}//3.实现:“获取数组中有效元素的数量的操作”size_t size()const{return _size;}//4.实现:“判断数组是否为空的操作”bool empty()const{return _size == 0;}};
}

三、模板模板参数

模板模板参数(Template Template Parameters):是指模板本身作为参数,用于将另一个模板传递给当前模板。

:暂时先了解一下概念即可)

------------模板特化------------

1. 什么是模板特化?

模板特化(Template Specialization):是模板机制的一个重要特性,允许针对特定的模板参数类型,提供模板的定制化实现。

  • 它允许针对特定类型,定制模板的行为,解决通用模板在特殊场景下的 “水土不服” 问题。
  • 当模板在某些特定类型下需要不同的行为或更高效的实现时,特化可以让代码更灵活、更贴合需求。

2. 为什么要使用模板特化?

在介绍的模板特化的时候,我们说模板特化是多么的厉害,但是口说无凭,模板特化真的有那么好吗?

下面的我们就来看一看模板特化的重要性。

#include <iostream>
using namespace std;//任务1:定义一个日期类
class Date
{
public:/*--------------成员变量--------------*/int _year;int _month;int _day;/*--------------成员函数--------------*///1.实现:“默认构造函数”Date(int y, int m, int d): _year(y), _month(m), _day(d){}//2.实现:“<运算符重载函数”bool operator<(const Date& other) const  //注意:用于比较两个日期的先后顺序{if (_year != other._year) return _year < other._year;      // 优先比较年份if (_month != other._month) return _month < other._month;  // 年份相同则比较月份return _day < other._day;								   // 年份和月份都相同则比较日期}};//任务2:定义比较函数模板
template<class T>
bool Less(T x, T y)
{return x < y;  //注意:依赖 T 类型的 operator< 实现
}int main()
{// ---------------- 基础类型比较(正确行为)----------------// 1. 实例化 Less<int>(int, int)// 调用内置的 int 类型的 < 运算符cout << Less(1, 2) << endl; // ---------------- 对象类型比较(正确行为)----------------// 2. 实例化 Less<Date>(Date, Date)// 调用 Date 类重载的 operator<Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl;  // ---------------- 指针类型比较(潜在问题)----------------// 3. 实例化 Less<Date*>(Date*, Date*)// 调用指针类型的 < 运算符(比较内存地址)Date* p1 = &d1;  // p1 指向 d1 的内存地址Date* p2 = &d2;  // p2 指向 d2 的内存地址cout << Less(p1, p2) << endl;  // 可能输出 0 或 1(取决于内存地址的随机分配)//注意:此处本意是比较对象内容,但实际比较的是指针地址return 0;
}

在这里插入图片描述

可以看到,Less 函数在大多数情况下都能正常比较,但在特殊场景中会得出错误结果。

在上述示例里:

  • p1 指向的 d1 显然小于 p2 指向的 d2 对象
  • 然而 Less 内部并没有比较 p1p2 所指向对象的内容
  • 而是比较了 p1p2 指针的地址,这就无法达成预期,进而出现错误

(哈哈,虽然博主在自己的VS上演示的结果没有出错,但是并不代表它没有问题呦)

这时,就需要对模板进行特化处理。也就是在原模板的基础上,针对特定类型,进行专门化的实现。

3. 模板特化有哪些?

模板特化的分类:

C++ 的模板特化可分为:函数模板特化类模板特化 两大类。

一、函数模板特化

函数模板特化的步骤

1. 先定义基础函数模板

  • 要特化函数模板,得先有一个通用的基础函数模板,它为各类型提供默认的泛型逻辑。

    • 比如:我们想实现 “比较两个值大小,返回 bool 结果” 的功能,先写通用模板:

       template <class T>bool Less(T left, T right) {return left < right; }
      
  • 这个模板能处理 intdouble自定义类(若重载了 < 运算符 )等类型的比较,但遇到指针类型时会因比较地址而非内容出问题,这就需要特化。


2. 添加特化声明与实现

  • 特化标识template<> 表明这是一个模板特化空尖括号表示不再推导模板参数
  • 明确特化类型在函数名后的尖括号里,写上要专门处理的特定类型(如:Date* 类型,就写 Less<Date*>
  • 保持形参匹配特化函数的形参列表,必须和基础函数模板的形参类型严格一致,否则编译器可能报错或匹配异常

前面我们提到,Less 函数在比较 p1p2 时,内部并未比较它们所指向对象的内容,而是直接比较了指针的地址,这与预期不符,会导致错误。

此时,需要通过模板特化来解决这一问题。

既然已经了解了模板特化的方法,接下来我们就按照上面的步骤对代码进行特化处理吧!

#include <iostream>
using namespace std;//任务1:定义一个日期类
class Date
{
public:/*--------------成员变量--------------*/int _year;int _month;int _day;/*--------------成员函数--------------*///1.实现:“默认构造函数”Date(int y, int m, int d): _year(y), _month(m), _day(d){}//2.实现:“<运算符重载函数”bool operator<(const Date& other) const  //注意:用于比较两个日期的先后顺序{if (_year != other._year) return _year < other._year;      // 优先比较年份if (_month != other._month) return _month < other._month;  // 年份相同则比较月份return _day < other._day;								   // 年份和月份都相同则比较日期}};//任务2:定义比较函数模板Less
template<class T>
bool Less(T left, T right)
{return left < right;  //注意:依赖 T 类型的 operator< 实现
}//任务3:对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}int main()
{// ---------------- 基础类型比较(正确行为)----------------// 1. 实例化 Less<int>(int, int)// 调用内置的 int 类型的 < 运算符cout << Less(1, 2) << endl; // ---------------- 对象类型比较(正确行为)----------------// 2. 实例化 Less<Date>(Date, Date)// 调用 Date 类重载的 operator<Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl;  // ---------------- 指针类型比较(潜在问题)----------------// 3. 实例化 Less<Date*>(Date*, Date*)// 调用指针类型的 < 运算符(比较内存地址)Date* p1 = &d1;  // p1 指向 d1 的内存地址Date* p2 = &d2;  // p2 指向 d2 的内存地址cout << Less(p1, p2) << endl;  //注意:调用特化之后的版本了,而不是走通用模板了return 0;
}

在这里插入图片描述

函数模板全特化

函数模板全特化:为函数模板的所有参数显式指定类型,完全覆盖通用逻辑。


  • 语法:

    template <> 		     //函数模板特化
    返回类型 模板函数名<特化类型>(参数列表) 
    { ... 
    }
    
  • 示例:通用函数模板用于比较两个值的大小,但对 const char*(C 风格字符串),默认会比较指针地址而非内容,所以需要特化:

#include <iostream>
#include <string>
using namespace std;/*--------------------- 通用模板(求最大值)---------------------*/
template <typename T>
T max_val(T a, T b)
{return a > b ? a : b;
}/*------------------ 针对const char*类型的全特化 ------------------*/
template <> // 函数模板全特化
const char* max_val<const char*>(const char* a, const char* b)  //按字符串字典序比较
{return strcmp(a, b) > 0 ? a : b; // 使用 C 风格字符串比较//注意:strcmp 返回值:a > b 则 >0,a < b 则 <0,相等则 0
}int main()
{/*----------------调用通用模板:比较int值----------------*/cout << "-----调用通用模板:比较int值-----" << endl;cout << max_val(10, 20) << endl;/*---------------------- 调用全特化版本:比较字符串的内容 ----------------------*/cout << "-----调用全特化版本:比较字符串的内容-----" << endl;const char* s1 = "apple";const char* s2 = "banana";cout << max_val(s1, s2) << endl;  //注意:自动匹配特化版本return 0;
}

在这里插入图片描述

函数模板偏特化

注意:C++不直接支持 函数模板偏特化(语法会报错),但可通过 函数重载 模拟类似效果。

  • 示例:max_val 对指针类型,比较指针指向的值,而非指针地址,通过重载实现:指针类型的 “偏特化”
#include <iostream>
using namespace std;// 通用函数模板:比较值
template <class T>
T max_val(T a, T b)
{return a > b ? a : b;
}// 重载版本:针对指针类型(模拟偏特化)
template <class T>
T* max_val(T* a, T* b)
{return *a > *b ? a : b;
}int main()
{int x = 10, y = 20;// 调用通用模板:比较 int 值cout << max_val(5, 3) << endl;  // 调用重载的指针版本:比较 *x 和 *yint* result = max_val(&x, &y);cout << *result << endl;  return 0;
}

在这里插入图片描述

原理:

  • 重载的 max_val(T* a, T* b) 并非严格意义的 “偏特化”,但利用函数重载决议,优先匹配指针类型的调用,达到 “针对部分类型定制” 的效果。
  • 若直接写函数模板偏特化语法(如:template <class T> T max_val<T*>(T* a, T* b) { ... }),编译器会报错,因此实际开发常用重载替代。

注意:

  • 虽然这种方式严格一点说并不能算作是函数模板特化,但是其实现简单、可读性高且易于书写。
  • 对于参数类型复杂的函数模板,使用特化反而可能会变得繁琐且容易出错,因此,不建议对函数模板进行特化,而应优先考虑使用函数重载类模板特化来替代。

二、类模板特化

类模板特化:为类模板的特定参数,定制类的实现,又分为:全特化和偏特化

类模板全特化

类模板全特化为类模板的所有参数显式指定类型,完全替换通用类的实现。


  • 语法:

    template <> 			//空模板参数列表,表示特化
    class 模板类名<特化类型>   //类模板特化
    { ... 
    }; 
    
  • 示例:假设有通用类模板 MyContainer,为 T=int, N=10 全特化:

#include <iostream>
using namespace std;// 通用类模板:存储 T 类型,容量为 N
template <class T, size_t N = 10>
class MyContainer
{
public:MyContainer(){cout << "通用类模板构造" << endl;}void print(){cout << "通用容器:存储类型 T,容量 " << N << endl;}
};// 类模板全特化:针对 T=int,N=10
template <>
class MyContainer<int, 10>
{
public:MyContainer(){cout << "全特化类构造(int, 10)" << endl;}void print(){cout << "特化容器:专门存储 int,容量 10(定制逻辑)" << endl;}
};int main()
{cout << "-----------调用通用类模板-----------" << endl;MyContainer<double, 5> c1;c1.print();  cout << "-----------调用全特化类-----------" << endl;MyContainer<int, 10> c2;   c2.print(); return 0;
}

在这里插入图片描述

关键说明:

  • 全特化类的实现与通用类完全独立,可自定义构造函数、成员函数等。
  • 当实例化 MyContainer<int, 10> 时,编译器优先选择全特化版本。

类模板偏特化

类模板偏特化对模板参数的部分类型特定条件进行特化,而非全部参数。


偏特化适用场景:

  • 参数数量特化:如模板有两个参数,特化其中一个。
  • 参数范围特化:如特化指针类型、引用类型、const 类型等。

第一种场景针对模板参数数量的偏特化

/*------------------------通用模板(两个参数)------------------------*/template <typename T1, typename T2>
class MyClass { ... };/*----------------------偏特化为第一个参数固定为int---------------------*/template <typename T2>		    // 保留第二个参数 T2
class MyClass<int, T2> { ... }; // 特化第一个参数为 int

第二种场景针对模板参数范围的偏特化

/*----------------------------通用模板----------------------------*/
template <typename T>
class MyClass
{
public:void print(T value){cout << "通用类型: " << value << endl;}
};/*------------------------针对指针类型的偏特化-------------------------*/template <typename T> // 仍保留一个模板参数 T,表示指针指向的类型
class MyClass<T*>     // 特化指针类型
{
public:void print(T* ptr) { cout << "指针地址: " << ptr << endl; }
};/*---------------------- 针对const类型的偏特化-----------------------*/template <typename T>
class MyClass<const T>  // 特化 const T 类型
{// 处理 const 类型的逻辑
};/*----------------------------- 调用 -------------------------------*/
MyClass<int> obj1;       // 通用类型,int
obj1.print(10);          // 输出:通用类型: 10MyClass<int*> obj2;      // 特化指针类型,int*
int x = 20;
obj2.print(&x);          // 输出:指针地址: 0x7fff...

示例:指针类型的偏特化:让 MyContainer 对指针类型 T*,定制存储逻辑(如:打印指针地址而非值)

#include <iostream>
using namespace std;// 通用类模板:存储 T 类型,容量为 N
template <class T, size_t N = 10>
class MyContainer
{
public:
MyContainer()
{cout << "通用类模板构造" << endl;}void print()
{cout << "通用容器:存储类型 T,容量 " << N << endl;}
};// 类模板偏特化:针对 T 是指针类型(T*)
template <class T, size_t N>
class MyContainer<T*, N> 
{
public:
MyContainer(T* data) : _data(data) {cout << "偏特化类构造(指针类型)" << endl;}void print() 
{cout << "存储指针:地址 = " << _data << endl;}
private:T* _data;
};int main() 
{
cout << "-----------调用通用类模板-----------" << endl;
MyContainer<double, 5> c1;c1.print();cout << "-----------调用指针类型的偏特化-----------" << endl;
int x = 100;MyContainer<int*> c2(&x); // 调用偏特化类(T=int*, N=默认 10)c2.print();  return 0;
}

在这里插入图片描述

说明:

  • 偏特化后,MyContainer<T*, N> 中的 T 仍为泛型(如:int),N 也保留默认值,但约束了 T 必须是指针类型。
  • 实例化 MyContainer<int*> 时,自动匹配偏特化版本。

回顾与总结:

Less 函数在比较 p1p2(指针)时,内部没有比较它们指向对象的实际内容,而是直接比较了指针的地址,这与我们想要比较对象内容的预期不符,会引发错误。

  • 这时,我们可以用模板特化解决问题,所以之前我们尝试过用 函数模板全特化 处理

  • 但是后来我们提到过:对于参数类型复杂的函数模板,特化过程容易变得繁琐、易错。

  • 所以,不建议直接对函数模板特化,更推荐优先用 函数重载类模板特化 替代。


前面我们已经演示了用函数重载解决指针比较问题,接下来就用类模板特化的方式,处理这个场景,让比较逻辑符合预期。

#include<vector>
#include<algorithm>
using namespace std;//任务1:定义一个日期类
class Date
{
public:/*--------------成员变量--------------*/int _year;int _month;int _day;/*--------------成员函数--------------*///1.实现:“默认构造函数”Date(int y, int m, int d): _year(y), _month(m), _day(d){}//2.实现:“<运算符重载函数”bool operator<(const Date& other) const  //注意:用于比较两个日期的先后顺序{if (_year != other._year) return _year < other._year;      // 优先比较年份if (_month != other._month) return _month < other._month;  // 年份相同则比较月份return _day < other._day;								   // 年份和月份都相同则比较日期}};//任务2:定义比较类模板Less(仿函数)
template<class T>
struct Less
{//1.重载()运算符,使该结构体可像函数一样调用bool operator()(const T& x, const T& y) const{return x < y;  //注意:依赖 T 类型的 operator< 实现}
};//任务3:比较类模板Less按照指针方式特化
template<>
struct Less<Date*>
{bool operator()(Date* x, Date* y) const{return *x < *y;}
};int main()
{// 创建三个日期对象(d2 < d1 < d3)Date d1(2022, 7, 7);Date d2(2022, 7, 6);Date d3(2022, 7, 8);// ---------------- 存储对象的 vector 排序(正确行为)----------------vector<Date> v1;v1.push_back(d1);v1.push_back(d2);v1.push_back(d3);// 使用 Less<Date> 作为比较器// 实例化 Less<Date>,调用 Date::operator< 比较对象内容sort(v1.begin(), v1.end(), Less<Date>());// ---------------- 存储指针的 vector 排序(潜在问题)----------------vector<Date*> v2;v2.push_back(&d1);  v2.push_back(&d2);  v2.push_back(&d3);  // 使用特化模板 Less<Date*> 作为比较器// 比较的是指针指向的对象内容(*x < *y),而非指针地址sort(v2.begin(), v2.end(), Less<Date*>());return 0;
}

错误情况:若未进行类模板Less的指针方式特化处理

在这里插入图片描述

正确情况:若进行了类模板Less的指针方式特化处理

在这里插入图片描述

------------分离编译------------

什么是分离编译?

分离编译:是一种软件开发中的编译策略,指将程序的不同部分(如:不同的源文件)分别编译为目标代码(.obj.o文件),最终再通过链接器将这些目标文件和依赖的库文件合并成可执行程序库文件的过程。


核心思想:

将大型程序拆解为多个独立编译单元(源文件),每个单元单独编译,减少重复编译的开销,提高开发效率


关键机制:

  • 声明与定义分离:头文件(.h)放声明,源文件(.cpp)放实现
  • 编译单元:每个.cpp文件及其包含的头文件构成独立编译单元
  • 符号决议:链接器负责解决跨文件的函数/变量引用

在这里插入图片描述

模板的分离编译要注意什么事情?

模板的分离编译是 C++ 中一个容易引发错误的复杂问题,主要源于模板实例化机制传统分离编译模型的不兼容


传统分离编译流程:

  1. 编译器独立处理每个.cpp文件,生成对应的.obj文件
  2. 链接器将所有.obj文件合并,解析未定义的符号(如:函数调用)

模板实例化机制:

  • 模板代码(如:template <class T> void func(T x))本身不是完整代码
  • 需要在使用时根据实参类型(如:int)实例化出具体代码(如:void func(int x)

矛盾点:

  • 若模板定义(如:func的实现)放在.cpp文件中,编译器编译该文件时无法得知未来会被哪些类型实例化,因此不会生成具体代码
  • 当其他文件(如:main.cpp)使用该模板时,编译器只能看到模板声明,无法找到对应实例化的定义,导致链接错误

常见错误示例:错误的分离编译结构

/*--------------------------func.h(声明模板)--------------------------*/
template <class T>
void func(T x);/*--------------------------func.cpp(定义模板)--------------------------*/
#include "func.h"template <class T>
void func(T x)
{/* ... */
}/*--------------------------main.cpp(使用模板)--------------------------*/
#include "func.h"
int main()
{func(42); // 编译时找不到 func<int> 的定义,链接错误!return 0;
}

怎么解决模板分离编译时带来的问题?

解决模板分离编译问题的方法主要有两种:

  • 将模板定义放在头文件中
  • 使用显式实例化

最推荐第一种方法。毕竟,解决模板分离编译问题的核心就是:让编译器在实例化模板时能同时看到声明与定义。

  • 将声明和定义写在同一个头文件中,从根源上避免分离编译带来的符号解析问题,是最简单直接且兼容性最好的方案。

1. 将模板定义放在头文件中

  • 原理:让编译器在使用模板的编译单元(如:main.cpp)时同时看到声明和定义,直接实例化代码。

  • 示例

    /*---------------------------func.h---------------------------*/template <class T>
    void func(T x) 
    { /* 直接在头文件中定义 */ 
    }/*---------------------------main.cpp---------------------------*/#include "func.h"
    int main() 
    {func(42); // 编译器在此处实例化 func<int>return 0;
    }
    
  • 优点:简单直接,无需额外操作。

  • 缺点

    • 头文件包含实现细节,可能导致代码膨胀。
    • 修改实现需重新编译所有包含该头文件的源文件。

2. 使用显式实例化

  • 原理在模板定义文件中显式指定需要实例化的类型,强制编译器生成对应代码。

  • 示例

    /*---------------------------func.h(声明模板)---------------------------*/
    template <class T>
    void func(T x);
    /*---------------------------func.cpp(定义模板并显式实例化)---------------------------*/
    #include "func.h"//1.定义模板
    template <class T>
    void func(T x)
    {/* ... */
    }//2.显式实例化 int 类型
    template void func<int>(int);/*---------------------------main.cpp(使用模板)---------------------------*/
    #include "func.h"
    int main()
    {func(42); // 使用已显式实例化的 func<int>return 0;
    }
  • 优点:保持分离编译结构,避免头文件包含实现。

  • 缺点

    • 需预先知道所有会被使用的实例化类型,不灵活。
    • 新增类型需修改.cpp文件并重新编译。

在这里插入图片描述

http://www.dtcms.com/a/315781.html

相关文章:

  • @【JCIDS】【需求论证】联合能力集成与开发系统知识图谱
  • 机器学习通关秘籍|Day 03:决策树、随机森林与线性回归
  • 【工程化】tree-shaking 的作用以及配置
  • Android Framework代码屏蔽未接来电振动及声音通知
  • DHTMLX重磅发布React Scheduler组件,赋能日程管理开发!
  • SELinux加固Linux安全
  • 将普通用户添加到 Docker 用户组
  • 第十七天:原码、反码、补码与位运算
  • RAFT:让语言模型更聪明地用文档答题
  • Java从入门到精通 - 集合框架(一)
  • 最长连续序列(每天刷力扣hot100系列)
  • FastDeploy2.0:报qwen2.embed_tokens.weight
  • 2.4 组件通信
  • 24. 前端-js框架-Vue
  • Occ3D: A Large-Scale 3D Occupancy Prediction Benchmark for Autonomous Driving
  • Python高级编程与实践:Python性能分析与优化
  • Java技术栈/面试题合集(3)-Java并发篇
  • 【功能测试】软件功能上线测试经验总结
  • 场外个股期权的额度为何受限?
  • java web 服务员点餐系统demo 比较完整的前端后端+mysql + 图片上传 练习
  • 从审批流到审计链:刻录系统的全周期管控技术解析
  • Spring MVC框架中DispatcherServlet详解
  • 【开源工具】基于Python的PDF清晰度增强工具全解析(附完整源码)
  • LeetCode算法日记 - Day 2: 快乐数、盛水最多容器
  • 力扣经典算法篇-43-全排列(经典回溯问题)
  • vite面试题及详细答案120题(01-30)
  • 普通树状数组
  • 《Node.js与 Elasticsearch的全文搜索架构解析》
  • Leetcode 13 java
  • 2025-08-05Gitee + PicGo + Typora搭建免费图床