C++20 requires子句函数重载解析分歧及标准判定问询
C++20概念自依赖场景的编译器分歧与标准解读
问题场景
当判断类型s是否满足概念c时,不同编译器出现行为分歧:
- Clang与MSVC认为重载
f(c auto)带来的自依赖属于硬错误,直接拒绝代码 - GCC将该重载的约束检查失败视为替换失败(SFINAE),仅丢弃该候选,代码最终通过
代码示例
void f(int); template<typename T> concept c = requires(T t) { f(t); }; struct s {}; void f(s); void f(c auto); static_assert(c<s>); // Clang/MSVC报错,GCC通过
各编译器错误信息
Clang错误信息
<source>:4:13: error: satisfaction of constraint 'requires (T t) { f(t); }' depends on itself 4 | concept c = requires(T t) { f(t); }; | ^~~~~~~~~~~~~~~~~~~~~~~ <source>:4:13: note: while substituting template arguments into constraint expression here 4 | concept c = requires(T t) { f(t); }; | ^~~~~~~~~~~~~~~~~~~~~~~ <source>:8:8: note: while checking the satisfaction of concept 'c<s>' requested here 8 | void f(c auto); | ^ <source>:8:8: note: while substituting template arguments into constraint expression here 8 | void f(c auto); | ^ <source>:4:29: note: while checking constraint satisfaction for template 'f<s>' required here 4 | concept c = requires(T t) { f(t); }; | ^ <source>:4:29: note: in instantiation of function template specialization 'f<s>' requested here <source>:4:29: note: in instantiation of requirement here 4 | concept c = requires(T t) { f(t); }; | ^~~~ <source>:4:13: note: while substituting template arguments into constraint expression here 4 | concept c = requires(T t) { f(t); }; | ^~~~~~~~~~~~~~~~~~~~~~~ <source>:10:15: note: while checking the satisfaction of concept 'c<s>' requested here 10 | static_assert(c<s>); | ^~~~ <source>:10:15: error: static assertion failed 10 | static_assert(c<s>); | ^~~~ <source>:10:15: note: because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression
MSVC错误信息
<source>(8): error C7608: atomic constraint should be a constant expression <source>(10): note: see reference to variable template 'bool c<s>' being compiled <source>(8): error C2131: expression did not evaluate to a constant <source>(8): note: failure was caused by a read of an uninitialized symbol <source>(8): note: see usage of 'c<s>'
标准规定与分析
C++20标准对于这种概念约束检查的循环依赖场景并没有给出明确的细化条款,这是导致编译器行为分歧的核心原因。我们可以从现有标准条款推导:
SFINAE原则:根据[temp.deduct]中的规定,模板参数替换过程中出现的非格式错误(如约束不满足)应触发SFINAE,仅丢弃当前候选而非拒绝整个代码。GCC正是基于此,将
f(c auto)的约束检查(依赖c<s>的结果)视为无法完成的替换失败,从而丢弃该重载,仅保留f(s)作为可行候选,最终判定c<s>成立。硬错误判定依据:Clang与MSVC则认为,这种循环依赖导致约束求值陷入无限递归,属于[temp.constr.decl]中提到的约束表达式无法形成有效的常量表达式,因此触发硬错误。标准中[expr.const]规定,常量表达式求值过程中若出现未定义的递归依赖,属于格式错误。
结论
目前该场景属于C++20标准的模糊地带,不同编译器的实现均有其标准依据。从代码意图来看,f(s)是明确的可行重载,GCC的处理更符合代码的直观预期;但从约束求值的严谨性来看,Clang与MSVC的硬错误判定也符合常量表达式的求值规则。
内容的提问来源于stack exchange,提问作者303
相关产品推荐
相关产品推荐

