https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67070
--- Comment #7 from Andrew Sutton <andrew.n.sutton at gmail dot com> --- We haven't evaluated constraints as expressions in a long time (since post-Rapperswil I think). I don't think this is a good idea, but mostly because I'm not sure what the instantiation/satisfaction semantics are. Consider: template<typename T> concept bool C() { return T::value; } template<typename T> void f(T); // #1 template<C T> void f(T); // #2 f(0); // ill-formed or #1? If you evaluate constraints as expressions, then how is C<T>() instantiated? If it's instantiated like a regular function, then the program should be ill-formed since we're in a different instantiation context (in exactly the same way this would fail for a function with a deduced return type). We might alternatively evaluate concept checks by always evaluating them in a SFINAE context, and defining, so that C<T>() returns true iff its constraint is satisfied. Here, T::value (for int) yields a substitution failure and is not satisfied. So #1 is chosen. But these semantics actually do something a little weird for !. It let's you write checks for when substitution fails or a constraint is not satisfied. template<typename T> requires !C<T>() void g(); g() is selected whenever C<T>() is not satisfied, or if substitution into C<T>() fails. Those semantics were not considered in the original design. A predicate constraint should evaluate a valid predicate. Substitution failure is a kind of higher-order property of the system. I'm also worried that evaluating constraints in this way would force us to consider extending the logical system to support negation. And it's not always obvious what the semantics of things like: !requires { typename T::type; } would mean. Is it that T::type must not exist? Is it that T::type is not required to exist? There are very good reasons to select both.