常用模板工具
内容简介
现代 C++ 模板常配合一些工具使用:别名模板、变量模板、类型萃取、if constexpr、SFINAE、concepts。这些工具用于让模板更清楚、更安全。
别名模板
给复杂模板类型起别名:
template <typename T>
using Vec = std::vector<T>;
Vec<int> numbers;常见于简化智能指针、回调、容器类型:
template <typename T>
using Unique = std::unique_ptr<T>;变量模板
C++14 引入变量模板,常用于编译期常量:
template <typename T>
constexpr bool always_false_v = false;标准库中常见 _v 结尾工具:
std::is_same_v<int, int> // true
std::is_integral_v<int> // true
std::is_pointer_v<int*> // true类型萃取
类型萃取用于在编译期检查类型性质。
template <typename T>
void print_number(T value)
{
static_assert(std::is_arithmetic_v<T>, "T must be a number");
std::cout << value << '\n';
}常见工具:
| 工具 | 说明 |
|---|---|
std::is_same_v<A, B> | 判断两个类型是否相同 |
std::is_integral_v<T> | 判断是否整数类型 |
std::is_floating_point_v<T> | 判断是否浮点类型 |
std::is_pointer_v<T> | 判断是否指针 |
std::remove_reference_t<T> | 去掉引用 |
std::decay_t<T> | 做类似函数传参后的类型退化 |
if constexpr
C++17 的 if constexpr 可以在编译期选择分支:
template <typename T>
void print_value(const T& value)
{
if constexpr (std::is_pointer_v<T>) {
if (value) {
std::cout << *value << '\n';
}
} else {
std::cout << value << '\n';
}
}和普通 if 不同,未选中的分支不会实例化。
SFINAE
SFINAE 全称是 Substitution Failure Is Not An Error。意思是模板替换失败时,不直接报错,而是把这个候选模板从重载集合中移除。
典型写法:
template <typename T>
std::enable_if_t<std::is_integral_v<T>, void>
print_integer(T value)
{
std::cout << value << '\n';
}这表示只有 T 是整数类型时,函数才参与匹配。
concepts
C++20 用 concepts 更直接表达类型约束:
template <typename T>
concept Integer = std::is_integral_v<T>;
template <Integer T>
void print_integer(T value)
{
std::cout << value << '\n';
}标准库也有概念,例如:
template <std::integral T>
T add(T a, T b)
{
return a + b;
}最佳代码实践
- C++20 前简单约束可用
static_assert或enable_if。 - C++20 后优先用 concepts 表达模板要求。
if constexpr适合根据类型选择实现分支。- 类型萃取用于底层工具代码,普通业务代码不要滥用。
错误用法
template <typename T>
void process(T value)
{
if (std::is_pointer_v<T>) {
std::cout << *value << '\n'; // 普通 if 仍会实例化,非指针类型会报错
}
}应使用 if constexpr。
注意事项
- SFINAE 很强,但可读性差;能用 concepts 时优先 concepts。
_t通常表示类型别名,例如std::remove_reference_t<T>。_v通常表示值,例如std::is_same_v<A, B>。