cs106l-lecture-4-2-templates-and-functions
Course: 斯坦福大学: C++标准库编程 | C++ Standard Library Programming
🎮 CS106L Lecture 4.2 — Templates and Functions
🏛 课程:Stanford CS106L — Standard C++ Programming Language
📍 主题:模板与函数(Templates and Functions)
🎯 目标:理解模板(Template)的工作原理、类型推导机制、谓词与 lambda 的使用方式,以及 C++20 Concepts 的改进思想。
🧭 课程定位
在上一讲中我们学习了:
“算法是独立于容器的逻辑。
迭代器是算法与容器的桥梁。”
但——
算法如何做到对“任意类型”都能工作?
答案就是 模板(Templates)。
📘 模板是 C++ 泛型编程的基础。
它让函数或类在编译期“生成”适合的类型版本。
🏷 关键词速记
Template|模板
Type Deduction|类型推导
Predicate|谓词(函数式条件)
Lambda|匿名函数
Concept|概念约束
Compile-time Polymorphism|编译期多态
🎮 30 秒课程摘要
“模板让 C++ 成为会‘变形’的语言。
你写一次逻辑,它在编译期为每种类型生成最优代码。”
📚 核心内容讲解
🧩 1️⃣ 模板的诞生
假设我们想写一个函数来返回两个数中较大的一个:
📦 示例 1:类型不泛化
int maxInt(int a, int b) {
return (a > b) ? a : b;
}
double maxDouble(double a, double b) {
return (a > b) ? a : b;
}问题:重复!
📦 改进:使用模板
template <typename T>
T myMax(T a, T b) {
return (a > b) ? a : b;
}🧩 讲解:
template <typename T>定义了类型参数编译器在调用时自动推导
T类型即:
myMax(3, 7)→ 生成int版本;myMax(2.5, 9.1)→ 生成double版本📤 输出:
7,9.1
🧩 2️⃣ 模板类型推导(Type Deduction)
C++ 会在调用时根据参数自动决定模板类型。
📦 示例 2:类型推导
std::cout << myMax(10, 42) << "\n"; // 推导为 int
std::cout << myMax(3.14, 2.71) << "\n"; // 推导为 double
std::cout << myMax('A', 'B') << "\n"; // 推导为 char
⚠️ 若类型不一致,会报错:
myMax(10, 3.14); // ❌ 类型不匹配
📦 解决方案:显式指定模板类型
myMax<double>(10, 3.14); // ✅ 强制使用 double
🧠 总结:
- 编译期生成类型安全版本
- 无运行时开销
- 提升可复用性
🧩 3️⃣ 模板函数重载与非模板函数
📦 示例 3:函数模板与普通函数共存
template <typename T>
void printType(T x) {
std::cout << "Template version\n";
}
void printType(int x) {
std::cout << "Non-template version\n";
}
printType(5); // 调用非模板版本
printType(3.14); // 调用模板版本
🧩 讲解:
若普通函数与模板都匹配 → 优先选择普通函数。
📤 输出:
Non-template version
Template version
🧩 4️⃣ 谓词(Predicate)与算法结合
谓词是“返回布尔值的函数”。
常用于算法中的条件筛选。
📦 示例 4:谓词函数
bool isEven(int x) {
return x % 2 == 0;
}
std::vector<int> v = {1, 2, 3, 4, 5, 6};
int cnt = std::count_if(v.begin(), v.end(), isEven);
std::cout << cnt;📤 输出: 3
🧩 讲解:
count_if第三个参数是一个函数指针- 函数模板允许将“逻辑条件”以函数形式传入算法
- 泛化行为的核心体现
🧩 5️⃣ Lambda 表达式(匿名函数)
C++11 引入 lambda 表达式,可直接在调用点定义小函数。
📦 示例 5:Lambda 替代谓词
int cnt = std::count_if(v.begin(), v.end(),
[](int x){ return x % 2 == 0; });
std::cout << cnt;📤 输出: 3
🧩 讲解:
[]→ 捕获列表()→ 参数{}→ 函数体- 可在局部作用域中定义内联函数
📦 Lambda 捕获示例
int threshold = 4;
std::for_each(v.begin(), v.end(),
[threshold](int x){
if (x > threshold) std::cout << x << " ";
});📤 输出: 5 6
🧩 捕获机制说明:
[=]捕获所有外部变量(按值)[&]捕获所有外部变量(按引用)[this]捕获当前类实例
🧩 6️⃣ 模板函数与 Lambda 的结合
📦 示例 6:模板算法 + Lambda
template <typename T, typename Func>
void forEach(const std::vector<T>& v, Func fn) {
for (auto& elem : v) fn(elem);
}
std::vector<int> nums = {1, 2, 3};
forEach(nums, [](int n){ std::cout << n * n << " "; });📤 输出: 1 4 9
🧩 讲解:
模板不仅能接收类型参数,也能接收“函数行为”。
→ 函数式泛型编程(Functional Generic Programming)
🧩 7️⃣ 模板错误与调试
模板报错通常非常长,因为:
- 错误在“实例化阶段”才出现;
- 编译器要展开模板定义。
📦 错误示例:
template <typename T>
void printVector(const T& container) {
for (auto x : container) std::cout << x << " ";
}
printVector(42); // ❌ 不是容器
🧩 错误信息会类似:
“no matching begin()/end() for int”
💡 解决思路:
- 明确模板期望的接口(如
begin()/end()) - 在错误提示中关注“instantiation”关键字
🧩 8️⃣ C++20 Concepts(显式接口约束)
C++20 引入 Concepts,让模板函数可以明确指定参数能力。
📦 示例 7:Concepts 限定
#include <concepts>
template <std::integral T>
T add(T a, T b) {
return a + b;
}
std::cout << add(3, 4); // ✅ OK
std::cout << add(3.14, 2.7); // ❌ 编译错误
🧩 讲解:
std::integral限定参数必须是整数类型错误信息更清晰:
“template constraint not satisfied: requires integral type.”
📦 自定义 Concept
template <typename T>
concept Addable = requires(T a, T b) { a + b; };
template <Addable T>
T myAdd(T a, T b) { return a + b; }🧩 Concept 使模板函数接口显式化,减少调试成本。
🧩 9️⃣ 泛型编程的哲学
“C++ 的模板不是语法糖,而是编译期多态。”
区别于 OOP 的运行时多态(virtual functions),
模板实现的是编译期多态:
- 编译时生成类型安全版本
- 无运行时开销
- 更高性能
🧠 记忆口诀
“模板生万象,编译期多态强;
Lambda 当谓词,概念显接口。”
🧪 实践练习
✅ 练习 1:泛型 Max 函数
实现 myMax,能对任意类型比较返回最大值。
✅ 练习 2:Lambda 过滤器
用 std::copy_if + lambda 筛选偶数到新容器中。
✅ 练习 3:Concept 限定模板
写一个只接受浮点类型的 average() 函数。
✅ 练习 4:函数模板 + 谓词组合
用模板写一个 filterVector(),接受任意容器与谓词函数。
🪄 创作与工程建议
- 模板代码应放在
.h文件中(编译期展开)。 - 避免模板嵌套过深(调试困难)。
- 使用
concepts明确接口需求。 - Lambda 捕获外部变量时要注意作用域与生命周期。
- 模板实例化过多会增加编译时间,可用
explicit instantiation优化。
✅ 小结表
| 主题 | 要点 | 示例 |
|---|---|---|
| 模板函数 | 编译期生成特化版本 | template<typename T> |
| 类型推导 | 自动确定模板类型 | myMax(3,4) |
| 谓词与算法 | 逻辑条件函数指针 | count_if(v, pred) |
| Lambda | 局部匿名函数 | [](int x){return x>0;} |
| Concepts | 模板参数约束 | template<std::integral T> |
“模板让 C++ 在性能与抽象之间找到平衡,
既能表达思想,又能贴近机器。”