c++ 20中的约束是标准化的,然后通过在原子约束上划分它们来检查是否满足。例如,约束E = E1 || E2
有两个原子约束E1
和E2
原子约束中的替换失败应视为原子约束的假值。
如果我们考虑一个示例程序,有concept Complete = sizeof(T)>0
检查正在定义的类T
:
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>(); //ok everywhere
g<void,int>(); //error in Clang
}
则函数f<void,int>()
满足要求,因为Complete<void>
由于替换失败只求值为false
,Complete<int>
求值为true
。
但是一个类似的函数g<void,int>()
使编译器产生分歧。GCC接受它,但Clang不接受:
error: no matching function for call to 'g'
note: candidate template ignored: substitution failure [with T = void, U = int]: invalid application of 'sizeof' to an incomplete type 'void'
void g() requires(sizeof(T)>0 || sizeof(U)>0) {}
演示:https://gcc.godbolt.org/z/zedz7dMGx
函数f
和g
不是真的相同,或者Clang在这里是错误的?
Clang bug #49513;情况和分析与这个答案相似。
sizeof(T)>0
是一个原子约束,所以[temp.constr. cn]原子)/3适用:
要确定是否满足原子约束,首先将形参映射和模板实参替换到其表达式中。如果替换导致无效的类型或表达式,则不满足约束。[…]
sizeof(void)>0
是无效表达式,因此约束不满足,约束继续求值到sizeof(U)>0
。
template<class T, class U>
void g() requires(requires { requires sizeof(T)>0; } || requires { requires sizeof(U)>0; }) {}