Appearance
C++20 concepts(约束与概念):语法、与 SFINAE 对比
一、为什么需要 concepts
模板报错晦涩 + enable_if 语法冗长可读性差。C++20 引入命名好的编译期谓词(concept),把“类型须满足的性质”提到接口上,报错变成“不满足概念 X”,也更可读地约束模板。同时也是其它高级特性的基础(ranges 由 concept 驱动)。
二、定义 concept + requires 子句
定义概念
cpp
// 简单概念:为谓词起名
template <typename T>
concept Integral = std::is_integral_v<T>;
// requires 表达式:要求某些表达式合法(探测的“官方”写法)
template <typename T>
concept HasSize = requires(T& t) { t.size(); }; // 表达式合法即可带返回类型约束:{ t.size() } -> std::convertible_to<std::size_t>;
约束模板的四种写法
cpp
// 1) 函数参数上用 concept(最可读)
template <Integral T>
T square(T x) { return x*x; }
Integral auto half(Integral auto x); // 缩写(constrained auto)
// 2) requires 子句尾部
template <typename T> requires Integral<T>
void f(T);
// 3) requires 嵌在 decltype/后置
template <typename T> void g(T) requires Integral<T>;三、概念是“半开的谓词”,可组合
cpp
template<typename T> concept Number = Integral<T> || FloatingPoint<T>;
template<typename T> concept TerseThreadsafe = requires(...) { ...; };概念的结果是编译期 bool 常量,可 &&/||/! 组复合,也可 static_assert(Concept<T>) 自测。
四、requires 表达式种类(detail,能报出更精准错误)
- 简单要求
t.size(); - 类型要求
typename T::value_type; - 复合要求
{ expr } noexcept -> Ret - 嵌套要求
requires C<A>;
面试若只让讲一层,讲「concepts 命名 + requires 做可读约束、可组合、报错清晰」足够;能做 examples 深讲加分。
五、concepts vs SFINAE/enable_if 对比(重要)
| SFINAE/enable_if | concepts/requires | |
|---|---|---|
| 可选性表达 | 冗长的 enable_if+x 组合 | 简洁、按名组合 |
| 诊断 | “no matching function…候选被排弃”(不强调为何) | “约束 X 未满足”指向明确概念 |
| 约束检查时机 | 替换过程中 | 语义前后、更清晰约束 |
| 表达“特性” | 用 trait | 用 concept(本质包装 trait+requires) |
| 可靠性 | 手动管理歧义 | requires 提供子集合规约(更精确) |
| 编译速度 | 有时慢 | 相近,诊断更好 |
关键收获:concept 本质还是编译期布尔谓词——其实现仍基于 type traits + requires 探测,可视为“给 SFINAE 能力披上可读接口 + 可复用命名”。
六、与 overload / 分派
concepts 可让重载按“更精炼概念”排序:如两个概念 Number 与 Integral,传入 int 都匹配…… 不总按条件简繁自动排序;规则是按概念约束**更严格(subsumption 包含关系)**者胜出,需小心定义保证者胜。题目常直接给两 concept 判断走哪个——记住“概念封装更紧(后继)的优先”。
七、示例应用(HPC 语境)
- 约束矩阵 Element 满足
requires(T& a){ a+a; a*a; }→ 泛型 kernel 需要可算术。 - ranges 视图靠 concepts 选迭代器类别管道与算法。
- 若团队要兼容旧编译器,可退回 enable_if/if constexpr;concept 需 C++20。
八、自测
- 写个
Addableconcept 要求a+b合法。 template<class T> requires std::integral<T> void f();与未 constrainedf同时存在时传 int 调哪个?—— constrained 版本优先(更受约束)。- concepts 是否运行期开销?—— 纯编译期,零运行成本。