常用模板工具

内容简介

现代 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_assertenable_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>