A template (actual) argument matches a template (formal) parameter if the latter is at least as specialised as the former.
template <Char> typename T is more specialised than template <typename> struct S. Roughly speaking, template <Char> accepts a subset of what template <typename> accepts (the exact definition of what "at least as specialised" actually means is rather involved, but this is a zeroth approximation).
This means that the actual argument can be used in all contexts where the formal parameter can be used. That is, for any type K for which T<K> is valid, S<K> is also valid (because S<K> is valid for any K).
So it is OK to substitute S for T.
If you do it the other way around:
template<typename T> concept Any = true;
template<typename T> concept Char = Any<T> && std::same_as<T, char>;
template<template<Any> class T> void foo();
template<Char> struct S { };
int main()
{
foo<S>();
}
then this is ill-formed, because (roughly) you can say T<int> but not S<int>. So S is not a valid substitute for T.
Notes:
- Why would we need this always-true concept
Any? What's wrong with simply saying template <template <typename> typename>? Well, that's because of a special rule: if the parameter is not constrained at all, constraints of the argument are ignored, everything goes.
- Why write
Any<T> && std::same_as<T, char>;? To illustrate a point. The actual rules do not evaluate the boolean values of constraints, but compare constraints as formulae where atomic constraints serve as variables, see here. So the formal reason is that S has a conjunction of a strictly larger (inclusion-wise) set of atomic constraints than T. If S had the same or a strictly smaller set, it would be well-formed. If two sets are not ordered by inclusion, then neither template is more specialised, and there is no match.