[
Note 2:
A logical negation expression (
[expr.unary.op]) is an atomic constraint;
the negation operator is not treated as a logical operation on constraints
. Furthermore, if substitution to determine
whether an atomic constraint is satisfied (
[temp.constr.atomic])
encounters a substitution failure, the constraint is not satisfied,
regardless of the presence of a negation operator
. [
Example 2:
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);
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);
template <class T> int f3(T) requires (!sad<typename T::type>);
int i3 = f3(42);
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);
Here,
requires (!sad<typename T::type>) requires
that there is a nested
type that is not
sad,
whereas
requires (!sad_nested_type<T>) requires
that there is no
sad nested
type. —
end example]
—
end note]