类模板的语法
1. 什么是类模板
类模板(class template)是 “类型的模板”——它将一个或多个类型参数或非类型参数抽象出来,让一个“通用的类框架”能在编译期被实例化成针对不同类型或不同值的具体类。
作用:通用代码复用、静态多态、在容器/工具类实现中最常见。
2. 基本语法
// 声明一个带 1 个类型参数 T 的类模板
template <typename T>
class MyClass {
public:// 成员变量T value;// 构造函数MyClass(const T& v) : value(v) {}// 成员函数void print() const {std::cout << value << std::endl;}
};
template <typename T>
:告诉编译器,接下来定义的是一个类模板,T
是一个占位类型。MyClass<T>
:当你写MyClass<int>
时,编译器会 生成一个名为 MyClass 的实际类。
3. 如何使用(实例化)
int main() {// 1) 指定类型参数MyClass<int> mi(42);mi.print(); // 输出 42MyClass<std::string> ms("Hello");ms.print(); // 输出 Hello// 2) C++17 结构化绑定 + CTAD(类模板参数推导)MyClass m2(3.14); // 自动推断 T 为 doublem2.print(); // 输出 3.14
}
显式实例化:
MyClass<int>
/MyClass<std::string>
都是针对不同类型生成的“真实类”。CTAD(Class Template Argument Deduction):C++17 起,如果构造函数参数能唯一推断模板参数,就可以省略
<...>
。
4. 多模板参数与非类型模板参数
4.1 多类型参数
template <typename Key, typename Value>
class Pair {
public:Key first;Value second;Pair(const Key& k, const Value& v) : first(k), second(v) {}
};
使用:
Pair<std::string,int> p("age", 30);
std::cout << p.first << ": " << p.second << std::endl;
4.2 非类型模板参数
// size 是一个整型常量(编译期已知)
template <typename T, int size>
class FixedArray {T data[size];
public:T& operator[](int i) { return data[i]; }int getSize() const { return size; }
};
使用:
FixedArray<double, 5> arr;
for(int i = 0; i < arr.getSize(); ++i)arr[i] = i * 1.1;
5. 模板的实现分离
头文件:
.hpp
或.h
中同时放声明和定义,或者.h:声明 +
#include "MyClass.tpp"
.tpp:模板定义(实现)
因为模板必须在使用点可见实现,才能实例化。
// MyClass.hpp
#pragma once
template<typename T>
class MyClass {T v;
public:MyClass(const T&);void print() const;
};
#include "MyClass.tpp"// MyClass.tpp
#include <iostream>
template<typename T>
MyClass<T>::MyClass(const T& _v) : v(_v) {}
template<typename T>
void MyClass<T>::print() const {std::cout << v << std::endl;
}
6. 模板特化
6.1 完全特化(full specialization)
对某个具体类型做专门实现:
// 通用版本
template<typename T>
struct TypeName { static std::string name() { return "Unknown"; } };// 针对 int 完全特化
template<>
struct TypeName<int> { static std::string name() { return "int"; } };// 用法
std::cout << TypeName<double>::name(); // Unknown
std::cout << TypeName<int>::name(); // int
6.2 偏特化(partial specialization)
对一组类型做专门实现(只针对模板类,函数模板不支持偏特化):
// 针对指针类型的偏特化
template<typename T>
struct TypeName<T*> {static std::string name() { return TypeName<T>::name() + "*"; }
};// 用法
std::cout << TypeName<int*>::name(); // int*
7. 模板模板参数(更高级)
允许把“模板本身”作为参数:
template< template<typename> class Container, typename T >
class Wrapper {Container<T> c;
public:void add(const T& v) { c.insert(c.end(), v); }
};// 使用 vector 作为 Container 模板
Wrapper<std::vector, int> w;
w.add(10);
8. 小结与注意事项
模板是在编译期展开:每种参数组合都会生成独立代码,可能导致可执行文件变大。
定义与声明要都可见:否则编译器无法实例化。
特化:完全特化和偏特化用法不同,偏特化只适用于类模板。
CTAD:C++17 支持类模板参数推导,调用时可省略模板参数列表。
避免虚函数+模板:模板类加虚函数会多一层复杂度,按需使用。