Concept 是参与重载决议和编译期筛选的类型契约,需描述“能力”而非仅包装 type trait;应复用标准库 concept(如 std::copyable、std::ranges::range),并理解其检查发生在模板参数推导之后。

直接用 concept 定义约束,再在模板参数位置写上概念名——不是“加个装饰”,而是真正参与重载决议和编译期筛选的类型契约。
定义 concept 时别只写 std::is_integral_v 这种单层 trait
单纯包装 type trait(比如 std::is_integral_v)虽然合法,但掩盖了语义。真正有用的概念应描述“能力”而非“标签”。
- 错误示范:
template—— 它不保证你能对concept BadIntegral = std::is_integral_v ; T做任何事,比如+或可能仍失败 - 推荐写法:用
requires表达式检查实际操作,例如templateconcept Addable = requires(T a, T b) { a + b; }; - 复合约束更实用:比如
templateconcept SignedIntegral = std::is_integral_v && std::is_signed_v && requires(T t) { ++t; };
函数模板中应用 concept 的三种写法效果不同
写法影响重载优先级和错误触发时机,不能混用还觉得“都一样”。
- 前置约束(最常用):
template—— 编译器在形参解析阶段就检查,失败报错最干净T add(T a, T b); -
requires子句(适合复用已有 concept):template—— 和前置写法行为一致,但语法上更显式requires Integral T mul(T a, T b); - 尾置
requires(慎用):template—— 仅当其他重载都不匹配时才考虑它,容易被忽略或绕过T sub(T a, T b) requires Integral ;
requires 表达式里写 { a std::convertible_to 而不只是 a
只写 a 只检查表达式语法合法;加上返回类型约束才能防止“返回 int 却被当 bool 用”的隐式转换漏洞。
- 松散约束:
requires(const T& a, const T& b) { a —— 允许返回任意可隐式转bool的类型(比如int),但可能掩盖逻辑错误 - 严格约束:
requires(const T& a, const T& b) { { a std::convertible_to—— 明确要求返回值语义是布尔判断; } - 注意:必须用双大括号包裹表达式,否则编译器会误判为简单要求而非复合要求
标准库 concept 不要自己重造,但得知道它们在哪、怎么查
头文件里有 std::regular、std::equality_comparable 等, 里还有 std::range、std::input_iterator。它们不是玩具,是生产级接口契约。
- 别重复定义
template—— 直接用concept Copyable = std::copy_constructible && std::assignable_from ; std::copyable - 查文档优先看 cppreference 的 “Concepts” 章节,而不是翻源码;每个标准 concept 都有明确的语义定义和满足条件
- 组合用法很常见:
template<:input_iterator it std::sentinel_for> Sent>是std::ranges::find的真实签名,不是示例
最容易被忽略的是:concept 检查发生在模板参数推导之后、函数体实例化之前。这意味着你无法靠 concept 拦住一个连 auto 都推导不出来的类型——它先得是个合法类型,才有资格被约束。这点和 static_assert 的位置与作用完全不同。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











