使用 C++20 Concepts 简化模板编程
Simplify C++ templates with concepts
2022年6月6日
一句话判断
C++20 Concepts 终于让模板编程有了像样的类型约束机制,Xcode 14 全面支持,如果你还在用 enable_if 写模板,该升级了。
这场 Session 讲了什么
Developer Tools 团队的 Alex 从一个 isOdd 函数模板的错误诊断问题出发,演示了 C++20 Concepts 如何改善泛型编程的体验。
传统的 C++ 模板没有显式的类型约束——当传入错误类型时,编译器报错指向模板内部而非调用处,开发者需要在错误信息里翻找真正的问题来源。Concepts 通过在模板声明阶段就约束允许的类型,让编译器能在模板实例化之前就报出清晰的错误。
Session 覆盖了 C++ 标准库提供的 concepts(integral、floating_point、equality_comparable 等)、自定义 concepts 的编写方式、requires 子句的用法,以及 concepts 在函数重载决议中的应用。最后提到了其他 C++20 特性如 consteval。
值得深挖的点
Concepts 解决的核心问题是错误信息的可读性。 没有 concepts 时,一个简单的类型错误(比如把 1.1 传给只接受整数的模板)会导致编译器在模板内部报错,错误信息可能有几十行。加了 integral concept 约束后,编译器直接告诉你”double 不满足 integral 约束”,精准定位到调用处。
标准库的 concepts 已经覆盖了大部分场景。 <concepts> 头文件提供了类型分类(integral、floating_point)、构造/析构(default_constructible、move_constructible)、比较(equality_comparable、totally_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