c++20 的 concepts 是编译期契约声明,用于提升模板错误信息可读性并显式约束模板参数;通过 concept 关键字定义谓词,支持直接约束或 requires 子句使用,标准库提供 std::integral 等常用概念,其错误提示精准、组合性强,但不替代 sfinae 且不处理值依赖约束。

什么是概念(Concepts)?先看它能解决什么问题
C++20 引入 Concepts,核心目的是让模板错误信息可读、约束条件显式化。以前写 std::sort 传入一个没有 operator 的类型,编译器报错会一路展开几十层模板调用栈,最终在某个内部 <code>__comp 处失败;用 Concepts 后,错误直接定位到“该类型不满足 std::totally_ordered”。
它不是运行时检查,也不影响生成代码——只是编译期契约声明。
如何定义并使用自定义概念
定义概念本质是写一个编译期谓词,判断某类型是否满足一组要求:
常见写法是用 concept 关键字 + requires 表达式:
template<typename t>
concept Addable = requires(T a, T b) {
{ a + b } -> std::same_as<t>;
};
</t></typename>
说明:
-
requires(T a, T b)声明两个同类型变量用于测试表达式 -
{ a + b }表示“这个表达式必须合法” -
-> std::same_as<t></t>进一步约束返回类型必须是T(而非int或临时对象) - 不要漏掉
std::same_as这类约束,否则std::string+char*可能意外满足Addable
使用时有两种方式:
- 在模板参数列表中直接约束:
template<addable t> T add(T a, T b) { return a + b; }</addable> - 在
requires子句中后置约束:template<typename t> T add(T a, T b) requires Addable<t> { ... }</t></typename>
前者更简洁,后者适合复用已有模板名或做复杂条件分支。
用标准概念快速约束常见需求
别重复造轮子。C++20 标准库已提供一批实用概念,比如:
-
std::integral:匹配所有整型(含char、long long等),比std::is_integral_v<t></t>更直观 -
std::floating_point:仅限float/double/long double -
std::default_constructible:能写T{}且不抛异常 -
std::copy_constructible:注意它隐含要求std::destructible,否则编译失败
陷阱提示:
-
std::regular要求类型同时满足std::equality_comparable+std::copy_constructible+std::assignable_from<t t></t>等,但不保证有默认构造函数——容易误以为“常规类型就该能T{}” -
std::totally_ordered要求operator 且满足全序关系,但编译器不会验证数学性质(比如传递性),只检查符号是否存在、是否可重载
概念与 SFINAE、std::enable_if 的实际差异
Concepts 不是 SFINAE 的替代品,而是更高层的抽象。两者最大区别在于错误传播行为:
- 用
std::enable_if,不满足条件时模板根本不可见,可能触发重载决议失败,报错仍可能指向“no matching function” - 用 Concepts,不满足时直接报“concepts not satisfied”,并列出未通过的具体要求(如 “
a + bis not well-formed”)
性能上无差别,都是编译期计算。但 Concepts 更易组合:
template<addable t std::integral u>
T scale_add(T a, T b, U factor) { return (a + b) * static_cast<t>(factor); }
</t></addable>
而等价的 SFINAE 写法需嵌套多层 std::enable_if_t,可读性迅速下降。
概念本身不能捕获“值依赖”约束(比如要求某个整型模板参数 > 0),这类仍得靠 static_assert 或 C++23 的 consteval 函数辅助。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











