C++ 模板核心内容与高频面试题汇总

本文整理自学习笔记,涵盖模板基础、特化、注意事项以及常见面试题,适合复习

一、模板基础

1.1 函数模板

cpp 复制代码
template <typename T>
T max(T a, T b) {
    return a > b ? a : b;
}
  • 编译器根据实参推导模板参数,生成具体函数(实例化)

  • 可显式指定模板参数:max<int>(1, 2)

1.2 类模板

cpp 复制代码
template <typename T>
class Stack {
    std::vector<T> data;
public:
    void push(const T& val) { data.push_back(val); }
    void pop() { data.pop_back(); }
    T& top() { return data.back(); }
};
  • 类模板的成员函数只有在使用时才会被实例化

  • 类模板不能隐式推导模板参数(C++17 起可通过构造函数推导,需显式指定)

1.3 模板参数的类型

  • 类型参数typename Tclass T

  • 非类型参数int Nsize_t N、指针、引用等(编译期常量)

  • 模板模板参数template <typename> class Container

二、模板特化

2.1 全特化

为特定类型提供完全不同的实现

cpp 复制代码
template <>
class Stack<bool> {
    // 特殊实现
};

2.2 偏特化

只限定部分模板参数(仅适用于类模板,函数模板无偏特化,可重载)

cpp 复制代码
template <typename T>
class Stack<T*> {  // 针对指针类型的偏特化
    // ...
};

2.3 函数模板重载 vs 特化

  • 函数模板不能偏特化,但可以用重载实现类似效果

  • 函数模板的全特化语法:

cpp 复制代码
template <>
const char* max<const char*>(const char* a, const char* b) { ... }

但通常推荐使用重载而非特化函数模板,因为重载解析规则更直观

三、注意事项与常见陷阱

3.1 模板的编译与分离编译

  • 模板的定义通常必须放在头文件中因为编译器在实例化时需要看到完整定义

  • 若想分离编译,可以显式实例化(.cpptemplate class Stack<int>;),但不够灵活

3.2 typename 与 class 的区别

  • 在声明模板参数时,两者等价

  • 但在依赖类型中,必须用 typename 告诉编译器这是一个类型:

cpp 复制代码
typename T::iterator it;   // 若没有 typename,编译器会认为 iterator 是静态成员

3.3 依赖名称

  • 模板中依赖模板参数的名称称为依赖名称。对于非依赖名称,编译器会在第一阶段进行查找

  • 使用 this->Base<T>:: 来限定依赖基类中的名称

  • 函数模板通常被隐式视为 inline,但这不是强制的,编译器自行决定。

四、高频面试题

Q1:函数模板与普通函数重载时,哪个优先?

A:普通函数优先。如果存在完全匹配的普通函数,则不会实例化函数模板

cpp 复制代码
void max(int, int);
template <typename T> void max(T, T);
max(1, 2);  // 调用普通函数

Q2:类模板的成员函数何时实例化?

A:只有被调用时才会实例化。这允许类模板只实现所需函数,未被使用的函数即使有错误也不会被编译 --按需实例化

Q3:typenameclass 在模板参数中有什么区别?

A :在声明模板参数时没有区别,但在依赖类型中必须用 typename,如 typename T::value_type

Q4:什么是模板的偏特化?举例说明

A :针对部分模板参数进行特化。例如 template <typename T> class Stack<T*> 是针对指针类型的偏特化----其他参数保持原来不变

Q5:如何避免模板代码膨胀?

A:将不依赖模板参数的代码抽取到非模板基类中,或将公共部分封装到普通函数中

Q6:std::enable_if 的原理是什么?

A :它基于 SFINAE。当条件为 true 时,enable_if 有一个公共成员 type,否则没有,导致模板实例化失败,从而移除候选

Q7:什么是"两阶段名称查找"?

A:模板在定义时对非依赖名称进行第一轮查找(不依赖模板参数),在实例化时对依赖名称进行第二轮查找

Q8:模板元编程是编译时还是运行时?

A:编译时。利用模板特化和递归在编译期完成计算,如计算斐波那契数列

Q9:template <template <typename> class Container> 的作用?

A :模板模板参数允许将一个模板作为参数传递,例如 Stack<int, std::vector>,但 std::vector 有两个模板参数(第二参数是分配器),需注意匹配

相关推荐
EasyGBS4 分钟前
从接入到稳定播放:无插件直播H5视频流媒体播放器EasyPlayer.js如何撑起Web端流媒体
开发语言·前端·javascript
FfHUCisI6 分钟前
GMP 调度器:Go 并发的心脏是如何跳动的
开发语言·golang·php
Zzzzmo_9 分钟前
Spring MVC
java·spring·mvc·cookie/session
cfm_291411 分钟前
Spring核心设计模式
java·spring·设计模式
Java小白笔记13 分钟前
Java 实现阿里云 OSS 文件上传链路:普通上传、秒传、分片与断点续传
java·开发语言·数据库·spring·阿里云
传奇开心果编程15 分钟前
【Rust入门知识点学与练】第9课:Vec 动态数组
开发语言·学习·rust
卢锡荣18 分钟前
单芯掌控多口互联|乐得瑞 LDR6020 PD3.1 多通道 Type‑C 控制 SOC 芯片
c语言·开发语言
2333!!!!!18 分钟前
rocket新手一些常见问题
java·开发语言
yume_sibai19 分钟前
02-Rust 所有权与借用深入解析(底层原理 + 借用检查器 + 生命周期 + 内部可变性)
开发语言·后端·rust
xxwxx__22 分钟前
深入理解 C++ STL:stack、queue 与 deque 从使用到底层实现全解析
开发语言·c++·算法