C++ 模板(Template)基础与应用
在 C++ 中,**模板(Template)**提供了一种在编译期间生成泛型代码的方法。它类似于其他语言里的“泛型”,允许函数或类在不同类型间复用,而不需要重复编写代码。
1. 函数模板
在没有模板之前,如果想写一个打印不同类型的函数,可能需要为每种类型写一次:
void Print(int value) {std::cout << value << std::endl;
}void Print(std::string value) {std::cout << value << std::endl;
}
这样显得很重复,如果有新的数据类型还要继续写新函数。
使用模板重构
template<typename T>
// 也可以写成 template<class T>
void Print(T value) {std::cout << value << std::endl;
}int main() {Print(5); // intPrint("Hello"); // const char*Print(5.5); // double
}
1. 显式指定模板类型
Print<int>(5);
这里我们显式告诉编译器模板类型
T
是int
。编译器会生成一个函数版本
void Print(int value)
。如果你想强制类型,也可以显式指定。
2. 自动类型推导
Print("hello"); Print(5.5f);
编译器会根据传入的参数自动推导模板类型:
"hello"
是const char*
,所以T = const char*
。5.5f
是float
,所以T = float
。
你无需手动写
Print<const char*>("hello")
或Print<float>(5.5f)
,编译器会自动生成对应的函数版本。
特点:
类型推导:模板类型
T
可以由编译器自动推导,也可以显式指定,例如Print<int>(5)
。延迟生成:模板函数并不是实际存在的函数,只有在调用时才会生成对应类型的函数。
灵活:可以适配任意类型,不用重复编写相同逻辑。
2. 类模板
模板不仅可以用于函数,还可以用于类。例如,我们希望创建一个在栈上存放固定长度数组的类,但数组大小在编译时才能确定:
// size 必须在编译时已知
template<int N>class Array {
private:int m_Array[N];
public:int GetSize() const { return N; }
};int main() {Array<5> array;std::cout << array.GetSize() << std::endl; // 输出 5std::cin.get();
}
调用模板后,编译器会生成对应的类:
class Array {
private:int m_Array[5];
public:int GetSize() const { return 5; }
};
泛型数组
如果希望数组支持任意类型:
template<typename T, int N>class Array {
private:T m_Array[N];
public:int GetSize() const { return N; }
};int main() {Array<int, 5> array; // int 类型数组,长度为 5std::cout << array.GetSize() << std::endl;std::cin.get();
}
Array<5> array;
模板参数
N
是编译期常量。编译器会生成固定大小的数组
M m_Array[N];
,无需运行时动态分配。内存连续,访问效率高。
编译器在实例化模板时检查
N
的值,如果写Array<int, 0>
或负数,会直接报错。避免运行时错误,提高安全性。
实际上,C++ 标准库中的 std::array<int, 5>
就是类似的实现。
3. 模板的使用建议
函数模板适用于逻辑相同但类型不同的函数,避免重复代码。
类模板适用于类型或大小在编译期可确定的对象,例如数组、容器等。
适度使用模板:过度模板化可能导致编译时间变长或错误信息难以理解。
总结
模板是 C++ 泛型编程的核心工具,它可以在编译期生成类型安全且高效的代码,减少重复、提高代码复用性,同时保持高性能。