Simplify C++ templates with concepts
System & Services 进阶 20m

使用 C++20 Concepts 简化模板编程

Simplify C++ templates with concepts

2022年6月6日

在 Apple 官方观看视频

一句话判断

C++20 Concepts 终于让模板编程有了像样的类型约束机制,Xcode 14 全面支持,如果你还在用 enable_if 写模板,该升级了。

这场 Session 讲了什么

Developer Tools 团队的 Alex 从一个 isOdd 函数模板的错误诊断问题出发,演示了 C++20 Concepts 如何改善泛型编程的体验。

传统的 C++ 模板没有显式的类型约束——当传入错误类型时,编译器报错指向模板内部而非调用处,开发者需要在错误信息里翻找真正的问题来源。Concepts 通过在模板声明阶段就约束允许的类型,让编译器能在模板实例化之前就报出清晰的错误。

Session 覆盖了 C++ 标准库提供的 concepts(integralfloating_pointequality_comparable 等)、自定义 concepts 的编写方式、requires 子句的用法,以及 concepts 在函数重载决议中的应用。最后提到了其他 C++20 特性如 consteval

值得深挖的点

Concepts 解决的核心问题是错误信息的可读性。 没有 concepts 时,一个简单的类型错误(比如把 1.1 传给只接受整数的模板)会导致编译器在模板内部报错,错误信息可能有几十行。加了 integral concept 约束后,编译器直接告诉你”double 不满足 integral 约束”,精准定位到调用处。

标准库的 concepts 已经覆盖了大部分场景。 <concepts> 头文件提供了类型分类(integralfloating_point)、构造/析构(default_constructiblemove_constructible)、比较(equality_comparabletotally_ordered)、可调用(invocable)等核心 concepts。大多数情况下不需要自己写。

requires 子句处理多条件组合。 当一个模板需要同时满足多个 concept 时,用 requires 子句加逻辑运算符组合。比如 requires equality_comparable<T> && default_constructible<T>,编译器会逐个验证。

代码片段

// 使用标准库 concept 约束模板类型
#include <concepts>

// 用 integral 替代 class 关键字
template<std::integral T>
bool isOdd(T value) {
    return value % 2 != 0;
}

// isOdd(1.1) → 编译错误:double 不满足 std::integral 约束
// 错误信息直接指向调用处,而非模板内部
// 使用 requires 子句组合多个 concepts
template<typename T>
requires std::equality_comparable<T> && std::default_constructible<T>
bool isDefaultValue(const T& value) {
    return value == T();
}

// 自定义 concept
template<typename T>
concept Sortable = requires(T a, T b) {
    { a < b } -> std::convertible_to<bool>;
    { a > b } -> std::convertible_to<bool>;
    std::swap(a, b);
};

template<Sortable T>
void sort(std::vector<T>& items) {
    // 只接受满足 Sortable 约束的类型
    std::sort(items.begin(), items.end());
}

最佳实践

  • 优先使用标准库的 concepts。 它们经过充分测试,语义清晰,其他开发者一看就懂。
  • 在模板声明处就用 concept 约束,而不是在函数体内 static_assert 前者的错误信息更精准,后者仍然会在模板内部报错。
  • 自定义 concept 时用 requires 表达式描述约束。 它比手写一堆 trait 检测更直观、更易维护。
  • 利用 concepts 做函数重载。 不同 concept 约束的函数模板可以构成重载集,编译器自动选择最匹配的版本,比 SFINAE 技巧简洁得多。

还有什么值得关注

  • C++20 的 consteval 关键字强制函数在编译期执行,适合做编译期计算优化
  • constexpr 的限制在 C++20 中进一步放宽,更多代码可以在编译期运行
  • Xcode 14 对 C++20 的支持还包括 ranges、coroutines 等特性
  • 推荐搭配阅读 C++ 标准库的 <concepts> 头文件,了解所有可用的 concepts
WWDC 2022