58 / 80 · C++20 · 约 10 分钟
Concepts 与 requires:约束可调用的接口
C++20 concepts 把模板所需的类型性质和表达式条件写到接口上。requires 表达式检查是否存在合法操作,requires 子句控制候选资格;它们改善诊断与重载选择,但不能证明运行期输入或业务语义正确。
表达式检查与候选约束分开
concept 是命名的编译期约束,例如标准库的 integral 表示整型性质。requires 表达式产生 bool,可检查某个表达式是否有效、某个嵌套类型是否存在,或结果是否满足另一概念。requires 子句则把这些条件附到模板声明上,约束不满足时该候选不适用。
因此 requires requires(T x) { x.size(); } 中两个关键字职责不同:前者引入子句,后者引入表达式。重复本身并非语法错误,不过把常用条件提取为清晰的命名 concept,通常更利于复用、诊断和正确建立约束之间的关系。
检查你真正会调用的形式
如果函数体在 const 对象上调用 size,concept 也应使用 const T&,不能只证明非 const 对象拥有同名函数。复合要求 { expression } -> convertible_to<size_t> 检查结果类型的转换能力;加 noexcept 还可以要求该表达式不抛异常。
示例的 HasSize 与函数体共享同一种调用形式,vector 满足约束,int 不满足。requires 中的参数只是检查用的记号,不会真的创建 T,也不会在运行期调用 size。约束可把失败提前暴露,却不是插入了一段隐藏的运行期校验代码。
语法满足不等于语义满足
编译器可以判断比较表达式是否合法,却不能一般性证明用户比较器具有传递性,或某个 size 实现真的返回元素数量。标准概念中存在语义要求时,程序作者仍需遵守;一个表达式能编译,只说明通过了可机械检查的一部分。
设计约束时应只要求算法实际需要的能力,避免强迫所有调用者使用某个具体容器。约束更强的重载也不是凭“条件文字更多”就必然胜出,原子约束与 subsumption 有严格规则。优先复用命名概念,而不要在多个位置复制一组看似等价但来源不同的条件。
容易答错的地方
- requires 不会验证运行期值,例如 integral 不能保证除数非零、索引在界内或整数加法不溢出。
- 不应认为任意无效表达式放入 requires 都只返回 false;在非模板等不适用的上下文中仍可能直接构成编译错误。
运行一个例子
最低标准 C++20 · 完整程序 · 下载 .cpp
#include <cassert>
#include <concepts>
#include <cstddef>
#include <iostream>
#include <vector>
template<class T>
concept HasSize = requires(const T& value) {
{ value.size() } -> std::convertible_to<std::size_t>;
};
template<HasSize T>
std::size_t count(const T& value) { return value.size(); }
int main() {
static_assert(HasSize<std::vector<int>>);
static_assert(!HasSize<int>);
std::vector<int> values{2, 4, 6};
assert(count(values) == 3);
std::cout << count(values) << '\n';
}
在本地编译
g++ -std=c++20 -Wall -Wextra -Wpedantic -pthread modern-concepts.cpp -o example && ./example预期结果
3
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
若 count 的契约要求 size 调用绝不抛异常,如何修改 concept?为什么不能只给 count 随意加 noexcept?
查看参考答案
把复合要求改为 { value.size() } noexcept -> std::convertible_to<std::size_t>;,使不满足要求的类型在候选阶段被排除。仅给 count 加 noexcept 不会让内部操作不抛异常;若异常真的逃出,就会调用 terminate。若还涉及结果转换,完整的 noexcept 契约也应检查该转换。
继续查证
标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。