Fed*_*dor 17 c++ language-lawyer c++-concepts c++20
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
}
Run Code Online (Sandbox Code Playgroud)
那么该函数f<void,int>()满足要求,因为由于替换失败而Complete<void>计算为,并且计算为。falseComplete<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) {}
Run Code Online (Sandbox Code Playgroud)
演示: https: //gcc.godbolt.org/z/zedz7dMGx
功能f和g不完全相同,还是 Clang 在这里错了?
这是Clang 错误 #49513;情况和分析与这个答案类似。
sizeof(T)>0是一个原子约束,因此[temp.constr.atomic]/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; }) {}
Run Code Online (Sandbox Code Playgroud)