concepts 通过 requires 子句明确定义类型约束,如 addable 要求 a + b 返回 t 类型,比 sfinae 更清晰;支持 std::same_as 或 std::convertible_to 控制返回类型严格性,可组合使用并需正确声明操作数变量。

Concepts 要求类型支持 operator+ 且返回可赋值类型
直接写约束比用 std::enable_if 清晰得多。比如想让模板类只接受能相加并存回自身的类型(如 int、double),定义 concept 如下:
template<typename t>
concept Addable = requires(T a, T b) {
{ a + b } -> std::same_as<t>;
};</t></typename>
关键点在于:{ a + b } -> std::same_as<t></t> 不仅检查表达式合法,还强制返回类型必须是 T(而非 int 接收 short 加法结果这类隐式提升)。若只要求“能加”,去掉 -> std::same_as<t></t> 即可,但容易放行意外类型。
- 别漏写
requires子句里的变量声明(T a, T b),否则编译器无法推导操作数类型 -
std::same_as是严格等价,若需允许const T&等引用形式,改用std::convertible_to<t></t> - 多个要求用换行分隔,不用逗号——语法错误常源于此
在模板类中用 requires 子句约束参数
把 concept 塞进类模板声明的末尾,比 SFINAE 更直白:
template<typename t>
requires Addable<t>
class Accumulator { /* ... */ };</t></typename>
或者更紧凑地写在模板参数列表后(C++20 推荐写法):
template<addable t>
class Accumulator { /* ... */ };</addable>
两种写法语义一致,但后者更简洁;若需组合多个 concept(如 DefaultConstructible && Addable),必须用第一种写法或用 requires 拼逻辑表达式。
- 不能在 partial specialization 里重复写
requires,必须用偏特化条件本身体现约束 - 类模板的成员函数不自动继承类模板的 concept 约束,需单独加
requires(例如void add(T x) requires Addable<t></t>) - 错误提示会直接显示 “
Tdoes not satisfyAddable”,比 SFINAE 的一长串 substitution failure 清晰得多
用 std::integral 等标准 concept 快速筛选基础类型
不必事事重造轮子。<concepts></concepts> 头文件已提供常用概念:
#include <concepts>
template<:integral t>
class IntWrapper { /* 只接受 int/long/char 等整型 */ };</:integral></concepts>
注意:std::integral 包含 bool 和字符类型,若业务上要排除 bool,得额外加条件:
template<typename t>
requires std::integral<t> && (!std::same_as<t bool>)
class StrictIntWrapper { /* ... */ };</t></t></typename>
-
std::floating_point不包含float _Complex等扩展类型,跨平台时需留意 -
std::default_constructible检查的是默认构造函数是否 public 且 non-explicit,不保证无异常(要强异常安全得另加noexcept约束) - 自定义 concept 若依赖标准 concept,记得显式
#include <concepts></concepts>,否则 clang/gcc 可能静默失败
Concept 冲突或过度约束导致实例化失败
常见现象:明明传了 int,却报错 “int does not satisfy MyConcept”。典型原因有:
- concept 定义里用了未声明的变量名(如
{ x + y }但没在requires参数列表声明x,y) - 误用
std::same_as代替std::convertible_to——例如short + short返回int,不满足std::same_as<short></short> - 模板类继承自另一个受约束的基类,但基类约束未在派生类中重新声明(C++20 不自动传递约束)
- 使用了
auto参数的 lambda 在 requires 表达式中,某些编译器(如旧版 GCC)支持不完整
调试技巧:把 concept 拆成最简版本(只留一个 { a + b }),逐步加条件定位哪一行触发失败。实际项目中,concept 的命名和注释比实现细节更重要——下次看到 SortableContainer,得立刻明白它要求 begin()/end() 和 operator,而不是靠猜。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











