Concepts 是 C++20 将模板隐式契约显性化的核心特性,支持 requires 约束、命名 concept 定义、auto 参数推导及类模板特化,显著提升接口清晰度、错误提示质量与代码可维护性。

Concepts 是 C++20 引入的核心特性,它让模板参数的约束从“运行时失败”或“编译器报一长串错误”的黑盒状态,变成**可读、可复用、可推导的显式契约**。用好 Concepts,不是给模板加个装饰,而是从根本上提升接口清晰度、错误提示质量和代码可维护性。
用 requires 表达式快速约束单个模板参数
最轻量的用法是直接在函数模板或类模板声明处使用 requires 子句,检查类型是否满足一组条件:
- 例如要求类型支持
+和==,且结果可转为bool:
template
requires std::is_arithmetic_v
requires(T a, T b) { { a + b } -> std::same_as
T add_if_equal(T x, T y) { return x == y ? x + y : x; }
这种写法适合一次性、小范围约束,无需提前定义 concept 名称,但可读性略低,复用性差。
定义命名 Concept 提升可读性与复用性
把常见约束封装成具名 concept,就像定义接口一样清晰:
立即学习“C++免费学习笔记(深入)”;
template
concept Addable = requires(T a, T b) {
{ a + b } -> std::same_as
};
template
concept EqualityComparable = requires(T a, T b) {
{ a == b } -> std::convertible_to
};
template
T sum(T a, T b) { return a + b; }
template
T combine_if_equal(T a, T b, U x, U y) {
return (x == y) ? a + b : a;
}
命名 concept 可组合(如 concept IntegralAddable = std::integral),便于文档化、测试和团队协作。
Concept 与 auto 推导结合:让函数参数更安全、更直观
C++20 允许在函数参数中直接使用 concept 名称替代 auto,这比传统模板更简洁,又比裸 auto 更严格:
void process(Addable auto x, Addable auto y) { /* ... */ }
// 调用时:
process(3, 5); // ✅ int 满足 Addable
process(3.14, 2.71); // ✅ double 满足 Addable
process("a", "b"); // ❌ 编译失败,明确提示:type 'const char*' does not satisfy 'Addable'
相比 template,它省去模板声明,避免意外接受不合法类型(比如传两个不同算术类型导致隐式转换歧义),错误信息也直指 concept 名称而非底层 SFINAE 细节。
Concept 约束类模板与特化选择
Concept 不仅用于函数,还能精准控制类模板的可用性,甚至参与偏特化选择:
template
class Container { /* 通用实现 */ };
template
class Container
template<:integral t>
class Container
注意:多个特化必须满足**偏序规则**(即一个 concept 必须严格强于另一个,如 std::integral 是 EqualityComparable 的子集),否则编译器会报歧义。合理设计 concept 层级,能让特化逻辑更健壮。
Concepts 不是语法糖,它是把模板的“隐式契约”显性化、结构化的过程。写模板前先想清楚“这个参数到底要能做什么”,把它写成 concept,后续所有使用都自动获得类型安全和清晰反馈。不复杂但容易忽略。










