模板基础
function/class templates、auto、type traits 概览。
模板基础
这一课你将能做到
- 编写函数模板与类模板,用不同实参类型实例化出各自独立的函数/类
- 解释 C++ 模板与 Java 泛型的本质区别(编译期实例化 vs 类型擦除)
- 用模板写一个通用「评测结果」容器,并理解为什么模板要放在头文件里
为什么要学
你每天都在用模板而可能没意识到:std::vector<int>、std::map<std::string, int>、std::unique_ptr<Report>、std::sort——全是模板。STL 能一套代码服务所有类型,靠的就是它。
C++ 模板和 Java 泛型解决同一个问题(类型安全的通用代码),实现路径完全相反:Java 泛型是类型擦除——编译后 List<String> 和 List<Integer> 都是 List,运行期不区分,不能 new T();C++ 模板是编译期实例化——vector<int> 和 vector<string> 生成两份完全独立的机器码,还能内联优化。理解这个差异,就理解了为什么 C++ 编译慢、二进制大,却几乎没有运行期泛型开销。
判题沙箱里「不同语言的提交用不同评测流程」这类需求,模板是最 C++ 的解法:一份流程代码,编译期生成 N 份特化实现,零虚函数开销。
先建立图像
不用模板:复制粘贴 N 份(改类型改到怀疑人生)
int maxInt(int a, int b) { return a > b ? a : b; }
double maxDouble(double a, double b) { return a > b ? a : b; }
string maxString(string a, string b) { ... }
用模板:写一份,编译器帮你生成
template <typename T>
T maxOf(T a, T b) { return a > b ? a : b; }
maxOf(1, 2) → 编译器实例化出 int 版
maxOf(1.5, 2.5) → 实例化出 double 版
模板不是一段运行的代码,是一份「生成代码的图纸」
跟着做
步骤 1:第一个函数模板
新建 templates.cpp:
#include <iostream>
#include <string>
template <typename T> // 声明模板参数 T:一个编译期才能确定的类型
T maxOf(const T& a, const T& b) { // 传 const 引用:大对象不复制
return a > b ? a : b;
}
int main() {
std::cout << maxOf(3, 7) << "\n"; // T = int(自动推导)
std::cout << maxOf(2.5, 1.5) << "\n"; // T = double
std::cout << maxOf(std::string("AC"),
std::string("WA")) << "\n"; // T = string,比较字典序
// 显式指定 T:自动推导不出来时用
std::cout << maxOf<double>(3, 7.5) << "\n";
// maxOf(3, 7.5); // 编译错误:int 和 double 推导冲突(T 不能既是又是)
return 0;
}
g++ -std=c++17 templates.cpp -o templates && ./templates
输出:
7
2.5
WA
7.5
maxOf(3, 7) 与 maxOf(2.5, 1.5) 编译后是两个不同的函数,可以用 nm 或看汇编验证——这就是「实例化」。
步骤 2:多类型参数与模板推导
#include <iostream>
#include <string>
// 两个模板参数:输入输出类型可以不同
template <typename R, typename T>
R convertTo(const T& value) {
return static_cast<R>(value);
}
// 返回类型依赖两个参数:用 auto 推导(C++14 起支持返回类型推导)
template <typename A, typename B>
auto multiply(A a, B b) -> decltype(a * b) { // decltype:取表达式的类型
return a * b;
}
int main() {
int i = convertTo<int>(3.9); // double → int,得 3(截断)
double d = convertTo<double>(42); // int → double
std::cout << i << " " << d << "\n";
std::cout << multiply(3, 0.5) << "\n"; // int * double → 1.5
return 0;
}