否定和德摩根定律不是 C++20 部分按约束排序的一部分

Ami*_*rsh 4 c++ demorgans-law language-lawyer c++-concepts c++20

约束偏序的规则是指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.
Run Code Online (Sandbox Code Playgroud)

这些规则基于原子约束约束规范化的定义:

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.
Run Code Online (Sandbox Code Playgroud)

否定(即!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
}
Run Code Online (Sandbox Code Playgroud)

虽然此代码因模棱两可而失败:

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);
}
Run Code Online (Sandbox Code Playgroud)

代码:https : //godbolt.org/z/RYjqr2


以上导致德摩根定律不适用于概念:

template<class P>
concept has_field_moo_but_not_foo
     = has_field_moo<P> && !has_field_foo<P>;
Run Code Online (Sandbox Code Playgroud)

不等同于:

template<class P>
concept has_field_moo_but_not_foo
     = !(has_field_foo<P> || !has_field_moo<P>);
Run Code Online (Sandbox Code Playgroud)

第一个将参与部分排序,而后者则不参与。

代码:https : //godbolt.org/z/aRhmyy


不将否定处理作为约束规范化的一部分的决定是否是为了简化编译器供应商的实现?或者试图支持它是否存在逻辑缺陷?

Bar*_*rry 6

不将否定处理作为约束规范化的一部分的决定是否是为了简化编译器供应商的实现?

是的。这概括为在编译器中需要 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
Run Code Online (Sandbox Code Playgroud)

特别要注意的区别f3f4。是否requires !sad<typename T::type>意味着没有sad嵌套类型,或者有一个不是嵌套类型sad?它实际上意味着后者,而对的约束f4意味着前者。