检查成员函数需区分存在性、const/non-const调用上下文及返回类型约束:requires(const T& t) { t.size(); } 匹配const成员,requires(T t) { t.size(); } 可能失败;返回类型须用{t.func()}->std::same_as<T>等约束。

直接结论:检查成员函数签名必须区分「存在性」「调用上下文(const/non-const)」和「返回类型约束」,只写 t.func() 是不够的,多数编译错误源于这三者之一没对齐。
用 requires 检查成员函数是否存在且可调用
最常见误区是以为 requires(T t) { t.func(); } 就够了——它只保证非 const 对象能调用,不覆盖 const 场景。若目标函数是 const 成员(比如 size() const),必须显式传入 const 引用:
-
requires(const T& t) { t.size(); }—— 正确匹配 const 成员函数 -
requires(T t) { t.size(); }—— 可能匹配失败,尤其当只有 const 版本时 - 不要写
requires { T::func(); },那是查静态成员函数,不是实例成员
用带箭头的表达式块约束返回类型
仅检查能否调用,不控制返回值,会导致误判。例如 size() 返回 int 但你期望 std::size_t,必须加类型约束:
-
{ t.size() } -> std::same_as<:size_t></:size_t>—— 要求返回类型精确匹配 -
{ t.size() } -> std::convertible_to<:size_t></:size_t>—— 允许隐式转换(如返回int) -
{ t[0] } -> std::same_as<int></int>—— 注意引用类型也要写全,int和不同</li> </ul> <H3>C++17 兼容方案:SFINAE + <code>std::declval
探测重载函数在不支持 Concepts 的项目中,需靠
decltype+std::declval把探测塞进替换上下文,否则直接写t.func(0)会触发硬错误:立即学习“C++免费学习笔记(深入)”;
- 正确写法:
decltype(std::declval<t>().resize(0), std::true_type{})</t>—— 逗号表达式确保推导出std::true_type - 错误写法:
sizeof(T{}.resize(0))——T{}要求默认可构造,且不在替换上下文,失败即报错 - 检测带默认参数的重载(如
resize(size_t, T = {}))时,std::declval<t>().resize(0)</t>仍有效,SFINAE 会接受任一可行重载
别忽略重载与 cv 限定的组合爆炸
一个类可能同时提供
begin()、begin() const、begin() &&,而requires默认只匹配其中一个。若你写的算法要同时处理 const 和非 const 输入,得显式组合:-
requires(T t) { t.begin(); } && requires(const T& t) { t.begin(); }—— 两者都得存在 - 不要指望单个
requires(T t) { t.begin(); }自动覆盖所有重载版本 - 若只检查
{ t.func() }而不写箭头约束,编译器不会抱怨返回void或bool,但后续逻辑可能崩
真正难的不是写出第一个
requires,而是想清楚你要约束的是“能调用”“能用 const 调用”“返回值能转成 X”还是“必须精确是 Y 类型”——漏掉任一维度,模板就可能在下游某个看似无关的地方静默编译失败。 - 正确写法:



















