C++20模板requires子句原子约束替换失败的编译器行为差异问询
C++20约束等效性与编译器行为问题解答
结论
f和g的约束按照C++20标准完全等价,观察到的行为分歧是Clang的实现存在错误,GCC的处理符合标准要求。
核心标准依据
- 约束在进行满足性检查前必须完成归一化处理,逻辑或表达式
E1 || E2会被拆分为两个独立的原子约束分别校验。 - 单个原子约束在模板参数替换过程中发生失败时,会直接被判定为
false,不会触发全局的编译错误。 - 原子约束的拆分规则与表达式是封装在concept内部,还是直接写在requires子句中没有关联,二者的归一化结果完全一致。
代码行为解析
示例代码如下:
template<class T> concept Complete = sizeof(T)>0; template<class T, class U> void f() requires(Complete<T> || Complete<U>) {} template<class T, class U> void g() requires(sizeof(T)>0 || sizeof(U)>0) {} int main() { f<void,int>(); // 所有编译器均编译通过 g<void,int>(); // Clang编译报错,GCC编译通过 }
f<void,int>()的校验逻辑
归一化后得到两个原子约束:Complete<void>和Complete<int>:
- 替换
T=void时,sizeof(void)属于非法操作,Complete<void>被判定为false - 替换
U=int时,sizeof(int)>0合法且结果为true,整个逻辑或约束成立,编译通过。
g<void,int>()的校验逻辑
归一化后得到的原子约束和f完全一致:sizeof(void)>0和sizeof(int)>0,校验逻辑没有任何区别,理应编译通过。
Clang的错误是因为它没有正确对requires子句中直接书写的逻辑或表达式做原子约束拆分,而是直接对整个表达式做替换,触发了sizeof(void)的硬编译错误,违反了C++20标准的约束校验规则。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

