C++ 开发手册C++

CHAPTER 06抽象层 · TEMPLATES

模板与泛型

template、SFINAE、concepts、模板特化与编译期计算。模板是 C++ 零开销抽象与泛型编程的核心。

开始阅读难度高阶先修第一、三章约2.5小时
06.1

函数模板

用 template <typename T> 参数化类型,编译器按实参推导 T,生成对应实例。一次定义,多种类型复用。

template <typename T>
T max(T a, T b) { return a > b ? a : b; }
int x = max(3, 5);          // T 推导为 int
double y = max(1.2, 3.4);   // T 推导为 double
零开销

模板在编译期生成专用代码,运行期无类型擦除开销,是 C++ 零成本抽象的典型代表。

06.2

类模板

类也能参数化,如 std::vector<T>。成员定义在类内时随类一起模板化。

template <typename T>
class Box {
  T value_;
public:
  Box(T v) : value_(v) {}
  T get() const { return value_; }
};
Box<int> b(42);
06.3

模板特化

为特定类型提供定制实现:全特化(指定全部参数)、偏特化(部分参数)。常用于优化或特殊行为。

template <typename T>
struct traits { static constexpr bool is_ptr = false; };
template <typename T>
struct traits<T*> { static constexpr bool is_ptr = true; }; // 偏特化
06.4

concepts 约束

C++20 的 concepts 给模板参数加语义约束,错误在调用处清晰报出,取代晦涩的 SFINAE。requires 表达约束。

#include <concepts>
template <std::integral T>        // 约束 T 为整数类型
T add(T a, T b) { return a + b; }
// add(1.0, 2.0);  // 编译错误:不满足 integral
优先 concepts

新代码用 concepts 代替 SFINAE 技巧,错误信息可读、意图清晰,且能作为函数重载的区分依据。

06.5

类型萃取

通过 type_traits 在编译期查询/变换类型(is_same、remove_reference、decay),配合 enable_if/concept 做条件实现。

#include <type_traits>
static_assert(std::is_integral_v<int>);
static_assert(!std::is_pointer_v<int>);
06.6

constexpr

constexpr 让函数在编译期求值(若参数均为编译期常量),把计算从运行期搬到编译期,零运行时成本。

constexpr int square(int n) { return n * n; }
constexpr int s = square(8);   // 编译期算出 64
consteval / if constexpr

C++20 consteval 强制编译期;if constexpr 在编译期按条件丢弃分支,常用于模板里的类型分发。

06.7

变参模板

变参模板接受任意数量模板/函数参数,常用递归展开或折叠表达式(C++17)处理。

template <typename... Ts>
auto sum(Ts... xs) { return (xs + ...); }   // 折叠表达式
int s = sum(1, 2, 3, 4);                    // 10
06.8

常见陷阱与 FAQ

模板报错信息太长看不懂?

老代码的 SFINAE 错误极晦涩。改用 concepts 约束参数,错误会精确指向"不满足某概念",大幅提升可读性。

模板定义在头文件里?

模板通常需在使用处实例化,所以定义多放在头文件(或用 .inl / 显式实例化)。否则链接期找不到实例化代码。

constexpr 函数必须编译期调用吗?

不必。constexpr 函数也能在运行期调用;只要结果用于编译期上下文(如数组大小、static_assert)且参数恒定,才在编译期求值。

06.9

实战练习与资源

延伸资源