1. 模板的本质是什么?发生在什么时期?
- 本质:模板是 C++ 中编译期多态(静态多态)的实现方式,也是泛型编程(Generic Programming)的基础。它本身不是可执行代码,而是编译器用来生成代码的蓝图(模具)。它将“数据类型”抽象为参数,从而避免为每种类型重复编写逻辑。
- 发生时期:发生在编译期(Compile-time)。编译器在遇到具体调用时,进行模板实例化(Instantiation),用具体类型替换模板参数,生成真正的函数或类代码。
- 重要特性(延迟实例化):对于类模板,只有被使用的成员函数才会被实例化。未被调用的成员函数即使存在语法错误,只要不涉及模板参数的具体语义,编译依然可能通过。
2. 模板的基本语法是什么?使用哪些关键字?
- 基本语法:
// 函数模板 template <typename T, int N> // T为类型参数,N为非类型参数 T func(T a) { return a + N; } // 类模板 template <class T> class MyClass { /* ... */ }; - 关键字:
template:声明模板的必需关键字。typename与class:在模板参数列表中,两者完全等价。推荐使用typename,语义更清晰。- 依赖类型:当涉及依赖于模板参数的类型(如
T::iterator)时,必须使用typename显式声明它是一个类型,例如typename T::value_type val;。 <>:尖括号,用于包裹模板参数列表。
3. 隐式实例化和显式实例化有什么区别?
| 维度 | 隐式实例化 (Implicit) | 显式实例化 (Explicit) |
|---|---|---|
| 触发者 | 编译器(根据代码调用自动推导) | 程序员(手动编写代码强制生成) |
| 语法 | max(1, 2); 或 MyClass<int> obj; | template int max<int>(int, int);template class MyClass<int>; |
| 特点 | 只有被调用的类型才会实例化;同一类型多次调用只实例化一次。 | 无论是否被调用,都会生成代码。 |
| 工程用途 | 常规使用。 | 1. 显式实例化定义:将模板实现隐藏在 .cpp 中,减少头文件暴露。2. 显式实例化声明( extern template class MyClass<int>;):C++11 引入,告诉编译器其他编译单元已实例化,本单元不要重复生成,极大缩短编译时间。 |
4. T, K...叫做什么?
- 统称:模板参数(Template Parameters)。
- 细分类:
T、K、V:类型参数(Type Parameters)。T代表 Type,K代表 Key,V代表 Value。...Args:模板参数包(Template Parameter Pack)。Args是名字,...表示零个或多个。int N:非类型参数(Non-type Parameter)。必须是编译期常量(整型、枚举、指针、引用,C++20 后支持浮点数等)。template <typename T> class Container:模板模板参数(Template Template Parameter)。
5. 普通函数和函数模板重载时,谁的优先级更高?
- 重载决议(Overload Resolution)规则:
- 精确匹配优先:如果普通函数和模板都能提供精确匹配,优先选择普通函数(普通函数不需要推导,更省编译时间)。
- 转换优先:如果普通函数需要隐式类型转换(如
int转double),而模板能推导出精确类型(如T推导为int),则优先选择模板。 - 强制调用模板:使用空模板实参列表
max<>(a, b);可以强制编译器只考虑模板。 - 特化优先:如果存在针对该类型的模板特化(Specialization),则特化版本优先于普通模板和普通函数。
- 总结记忆:在匹配程度相同的前提下,优先级为:普通函数 > 模板特化 > 普通模板。
6. ...Args叫做什么?干嘛用的?
- 名称:可变参数模板(Variadic Templates)(C++11 引入)。
- 组成部分:
template <typename... Args>:Args是模板参数包。void func(Args... args):args是函数参数包。
- 作用:允许模板接受任意数量、任意类型的参数。它是实现完美转发(
std::forward)、std::thread、std::make_unique等标准库设施的基石。 - 如何展开(C++17 前后差异):
- C++11/14 方式:递归展开。需要写一个递归终止函数(处理最后一个参数),然后不断剥离第一个参数递归调用。
- C++17 方式:折叠表达式(Fold Expressions)。极大简化了代码,例如:
template <typename... Args> auto sum(Args... args) { return (args + ...); // 折叠表达式,自动展开为 args1 + args2 + ... } - 使用
sizeof...(Args)可以获取参数个数。
7. 类模板怎么使用?
- 基本使用:
template <typename T> class Box { T content; public: Box(T c) : content(c) {} T get() { return content; } }; - 实例化:
- C++17 之前:必须显式指定类型。
Box<int> b(10); - C++17 及以后(CTAD):支持类模板参数推导(Class Template Argument Deduction)。
Box b(10);(编译器自动推导为Box<int>)。
- C++17 之前:必须显式指定类型。
- 成员函数类外定义:必须重新加上
template声明。template <typename T> T Box<T>::get() { return content; } - 类模板的特化:
- 全特化(Full Specialization):
template <> class Box<bool> { ... };(针对特定类型完全重写)。 - 偏特化(Partial Specialization):
template <typename T> class Box<T*> { ... };(针对指针类型等特定模式重写)。
- 全特化(Full Specialization):
💡 进阶补充:
在现代 C++ 工程与面试中,除了上述基础,还需掌握 SFINAE(替换失败并非错误)、Concepts(C++20 概念约束) 以及 模板元编程(TMP),这些是区分 C++ 初级与高级开发者的重要分水岭。