Negation 和 De Morgan 定律不是 C++20 约束部分排序的一部分
Negation and De Morgan's Law not part of C++20 partial ordering by constraints
partial ordering by constraints的规则指AND和OR,不指NOT:
13.5.4 Partial ordering by constraints [temp.constr.order]
(1.2) ...
- The constraint A ∧ B subsumes A, but A does not subsume A ∧ B.
- The constraint A subsumes A ∨ B, but A ∨ B does not subsume A.
这些规则基于atomic constraints and constraints normalization的定义:
13.5.3 Constraint normalization [temp.constr.normal]
1 The normal form of an expression E is a constraint that is defined
as follows:
(1.1) The normal form of an expression ( E ) is the normal form of E.
(1.2) The normal form of an expression E1 || E2 is the disjunction
of the normal forms of E1 and E2.
(1.3) The normal form of an expression E1 && E2 is the conjunction
of the normal forms of E1 and E2.
否定(即!E1)没有特别处理。
因此下面的代码正确地使用了偏序:
void foo(auto i) requires std::integral<decltype(i)> {
std::cout << "integral 1" << std::endl;
}
void foo(auto i) requires std::integral<decltype(i)> && true {
std::cout << "integral 2" << std::endl;
}
int main() {
foo(0); // integral 2
}
虽然此代码因歧义而失败:
template<typename T>
concept not_integral = !std::integral<T>;
template<typename T>
concept not_not_integral = !not_integral<T>;
void foo(auto i) requires not_not_integral<decltype(i)> {
std::cout << "integral 1" << std::endl;
}
void foo(auto i) requires std::integral<decltype(i)> && true {
std::cout << "integral 2" << std::endl;
}
int main() {
foo(0);
}
代码:https://godbolt.org/z/RYjqr2
以上导致德摩根定律不适用于概念:
template<class P>
concept has_field_moo_but_not_foo
= has_field_moo<P> && !has_field_foo<P>;
不等同于:
template<class P>
concept has_field_moo_but_not_foo
= !(has_field_foo<P> || !has_field_moo<P>);
第一个会参与部分排序,而后者不会。
代码:https://godbolt.org/z/aRhmyy
决定不将否定处理作为约束规范化的一部分,是为了简化编译器供应商的实施吗?还是试图支持它存在逻辑缺陷?
Was the decision, not to handle negation as part of constraint normalization, taken in order to ease the implementation for compiler vendors?
是的。这概括为需要编译器中的 SAT 求解器。
在 [temp.constr.op]/5 中添加了一个例子来证明这一点,尽管它没有提供决定的理由:
template <class T> concept sad = false;
template <class T> int f1(T) requires (!sad<T>);
template <class T> int f1(T) requires (!sad<T>) && true;
int i1 = f1(42); // ambiguous, !sad<T> atomic constraint expressions ([temp.constr.atomic])
// are not formed from the same expression
template <class T> concept not_sad = !sad<T>;
template <class T> int f2(T) requires not_sad<T>;
template <class T> int f2(T) requires not_sad<T> && true;
int i2 = f2(42); // OK, !sad<T> atomic constraint expressions both come from not_sad
template <class T> int f3(T) requires (!sad<typename T::type>);
int i3 = f3(42); // error: associated constraints not satisfied due to substitution failure
template <class T> concept sad_nested_type = sad<typename T::type>;
template <class T> int f4(T) requires (!sad_nested_type<T>);
int i4 = f4(42); // OK, substitution failure contained within sad_nested_type
请特别注意 f3
和 f4
之间的区别。 requires !sad<typename T::type>
是指没有 sad
嵌套类型,还是存在不是 sad
的嵌套类型?其实就是后者的意思,而f4
上的约束就是前者。
partial ordering by constraints的规则指AND和OR,不指NOT:
13.5.4 Partial ordering by constraints [temp.constr.order] (1.2) ... - The constraint A ∧ B subsumes A, but A does not subsume A ∧ B. - The constraint A subsumes A ∨ B, but A ∨ B does not subsume A.
这些规则基于atomic constraints and constraints normalization的定义:
13.5.3 Constraint normalization [temp.constr.normal] 1 The normal form of an expression E is a constraint that is defined as follows: (1.1) The normal form of an expression ( E ) is the normal form of E. (1.2) The normal form of an expression E1 || E2 is the disjunction of the normal forms of E1 and E2. (1.3) The normal form of an expression E1 && E2 is the conjunction of the normal forms of E1 and E2.
否定(即!E1)没有特别处理。
因此下面的代码正确地使用了偏序:
void foo(auto i) requires std::integral<decltype(i)> {
std::cout << "integral 1" << std::endl;
}
void foo(auto i) requires std::integral<decltype(i)> && true {
std::cout << "integral 2" << std::endl;
}
int main() {
foo(0); // integral 2
}
虽然此代码因歧义而失败:
template<typename T>
concept not_integral = !std::integral<T>;
template<typename T>
concept not_not_integral = !not_integral<T>;
void foo(auto i) requires not_not_integral<decltype(i)> {
std::cout << "integral 1" << std::endl;
}
void foo(auto i) requires std::integral<decltype(i)> && true {
std::cout << "integral 2" << std::endl;
}
int main() {
foo(0);
}
代码:https://godbolt.org/z/RYjqr2
以上导致德摩根定律不适用于概念:
template<class P>
concept has_field_moo_but_not_foo
= has_field_moo<P> && !has_field_foo<P>;
不等同于:
template<class P>
concept has_field_moo_but_not_foo
= !(has_field_foo<P> || !has_field_moo<P>);
第一个会参与部分排序,而后者不会。
代码:https://godbolt.org/z/aRhmyy
决定不将否定处理作为约束规范化的一部分,是为了简化编译器供应商的实施吗?还是试图支持它存在逻辑缺陷?
Was the decision, not to handle negation as part of constraint normalization, taken in order to ease the implementation for compiler vendors?
是的。这概括为需要编译器中的 SAT 求解器。
在 [temp.constr.op]/5 中添加了一个例子来证明这一点,尽管它没有提供决定的理由:
template <class T> concept sad = false; template <class T> int f1(T) requires (!sad<T>); template <class T> int f1(T) requires (!sad<T>) && true; int i1 = f1(42); // ambiguous, !sad<T> atomic constraint expressions ([temp.constr.atomic]) // are not formed from the same expression template <class T> concept not_sad = !sad<T>; template <class T> int f2(T) requires not_sad<T>; template <class T> int f2(T) requires not_sad<T> && true; int i2 = f2(42); // OK, !sad<T> atomic constraint expressions both come from not_sad template <class T> int f3(T) requires (!sad<typename T::type>); int i3 = f3(42); // error: associated constraints not satisfied due to substitution failure template <class T> concept sad_nested_type = sad<typename T::type>; template <class T> int f4(T) requires (!sad_nested_type<T>); int i4 = f4(42); // OK, substitution failure contained within sad_nested_type
请特别注意 f3
和 f4
之间的区别。 requires !sad<typename T::type>
是指没有 sad
嵌套类型,还是存在不是 sad
的嵌套类型?其实就是后者的意思,而f4
上的约束就是前者。