目录

一,模板参数列表

类型模板参数(Type Template Parameter)

非类型模板参数(Non-type Template Parameter)

二,模板特化和偏特化(Specialization and partial specialization)

1.核心概念与区别(Core concepts and differences)

2.模板特化(Specialization)

3.偏特化 (Partial Specialization)

三,声明和定义分离

        1.核心挑战:模板的编译模型

2.解决方案

方案 2:.ipp / .tpp 包含法


一,模板参数列表

  1. 类型模板参数(Type Template Parameter)

    • class T 或 typename T 表示 T 是一个类型占位符,在实例化时会被具体类型(如 intstring)替换。
    • 使用 class 或 typename 在此处等价,但 typename 更通用(尤其在嵌套依赖类型中)。
  2. 非类型模板参数(Non-type Template Parameter)

    • size_t N = 10 表示 N 是一个编译期常量值,类型为 size_t(通常是整数类型),默认值为 10
    • 非类型参数必须是整数、枚举、指针或引用等可在编译期确定的值。
    • 代表array(array相对于数组越界检查严格,数组是抽查啊,功能和数组一样)
      template<class T, size_t N = 10>//c++20之前可以size_t(不可以其他的),
      //但是在c++20就可以其他的了
      class array
      {
      public:
      	T& operator[](const size_t x)
      	{
      		assert(x < N);
      		return a[x];
      	}
      private:
      	T a[N];
      };

二,模板特化和偏特化(Specialization and partial specialization)

在 C++ 中,模板特化(Specialization) 和 偏特化(Partial Specialization) 是泛型编程的核心技术,用于为特定类型或类型模式提供定制化实现。以下是深度解析:

1.核心概念与区别(Core concepts and differences)

特性 全特化 (Full Specialization) 偏特化 (Partial Specialization)
定义 所有模板参数指定具体类型的完全定制实现 仅对部分参数特化,保留其他参数的泛型特性
适用对象 类模板、函数模板 仅类模板(函数模板不支持偏特化)
语法 template<> class MyClass<int> {...} template<class T> class MyClass<T*> {...}
匹配优先级 最高优先级 次高于通用模板但低于全特化

2.模板特化(Specialization)

        跟函数重载差不多,有现成的先调用现成的,没有再找匹配的,最后就是模板。

template<class T>
class A
{
public:
	A()
	{
		std::cout << "class A" << std::endl;
	}
private:
	T a1;
};
template<>
class A<int>
{
public:
	A()
	{
		std::cout << "class A<int>" << std::endl;
	}
};
template<>
class A<int*>
{
public:
	A()//如果要传参推荐( int* const &x)
	{
		std::cout << "class A<int*>" << std::endl;
	}
};
int main()
{
	A<char> b;//class A
	A<int> a;//class A<int>
	A<int*> d;//class A<int*>
	return 0;
}

3.偏特化 (Partial Specialization)

        这个主要是指针和引用(泛型)

template<class T, class T1>
class B
{
public:
	B()
	{
		std::cout << "class B" << std::endl;
	}
};
template<class T, class T1>
class B<T*,T1*>
{
public:
	B()//如果要传参推荐( T* const &x1,T1* const &x1)
	{
		std::cout << "class B<T*,T1*>" << std::endl;
	}
};
template<class T, class T1>
class B<T&, T1&>
{
public:
	B()
	{
		std::cout << "class B<T&, T1&>" << std::endl;
	}
};
int main()
{
	pz::B<int, int> x;//class B
	pz::B<int*, int*>y;//class B<T*, T1*>
	pz::B<int&, int&>z;//class B<T&, T1&>
    return 0;
}

三,声明和定义分离

        1.核心挑战:模板的编译模型

  • 根本问题:模板是编译期生成的代码蓝图,编译器必须在实例化时看到完整定义
  • 传统分离后果:将定义放在 .cpp 文件会导致链接器报 undefined reference
  • 根本原因:模板实例化发生在调用点,若定义不可见则无法生成机器码

2.解决方案

方案 1:显式实例化(Explicit Instantiation)

适用场景:已知模板会被哪些类型使用

// myarray.h(声明)
 template<typename T> 
class MyArray 
{ 
public: void add(T item); // 仅声明 }
; 
// myarray.cpp(定义 + 显式实例化)
template<typename T> 
void MyArray<T>::add(T item)
{ /* 实现 */ } // 显式实例化所需类型(编译器在此生成代码) 
template class MyArray<int>; // 生成 int 版本
template class MyArray<double>; // 生成 double 版本

优点

  • 实现真正分离(.h 纯声明,.cpp 含定义)
  • 控制编译产物体积(仅实例化指定类型)

缺点

  • 不支持未知类型(如用户自定义类)
  • 维护成本高(需预判所有使用类型)

方案 2:.ipp / .tpp 包含法

适用场景:保持头文件简洁性

// myarray.h
template<typename T> 
class MyArray
{
public: 
void add(T item); 
}; // 包含定义文件(注意顺序!) 
#include "myarray.ipp" // 在类声明后立即包含
 // myarray.ipp(实现) 
template<typename T> 
void MyArray<T>::add(T item) 
{ 
// 实现细节
 }

关键细节

  1. 使用 .ipp/.tpp 扩展名区分模板实现文件
  2. 头文件末尾包含实现(确保类声明先被解析)
  3. 编译时视为同一翻译单元(解决实例化问题)

更多推荐