An *atomic constraint* is formed from
an expression E
and a mapping from the template parameters
that appear within E to
template arguments involving the
template parameters of the constrained entity,
called the *parameter mapping* ([temp.constr.decl]).

[ Note ]

Two atomic constraints are
*identical*
if they are formed from the same
expression
and the targets of the parameter mappings are equivalent
according to the rules for expressions described in [temp.over.link].

To determine if an atomic constraint is
*satisfied*,
the parameter mapping and template arguments are
first substituted into its expression.

If substitution results in an invalid type or expression,
the constraint is not satisfied.

Otherwise, the lvalue-to-rvalue conversion
is performed if necessary,
and E shall be a constant expression of type bool.

[ Example

: *end example*

]template<typename T> concept C = sizeof(T) == 4 && !true; // requires atomic constraints sizeof(T) == 4 and !true template<typename T> struct S { constexpr operator bool() const { return true; } }; template<typename T> requires (S<T>{}) void f(T); // #1 void f(int); // #2 void g() { f(0); // error: expression S<int>{} does not have type bool } // while checking satisfaction of deduced arguments of #1; // call is ill-formed even though #2 is a better match—