C++20 allows the program to specify concept for template template argument. For example,
#include <concepts>
template <typename T> concept Char = std::same_as<T, char>;
template <typename> struct S {};
template <template <Char U> typename T, typename U> T<U> foo() { return {}; }
int main() { foo<S, int>(); }
the first template argument of the function foo
is expected to be a single argument template.
The concept Char
is defined to be true only the type char
, so an attempt to satisfy it for int
shall fail. Still above program is accepted by all compilers: https://gcc.godbolt.org/z/PaeETh6GP
Could you please explain, why the concept in template template argument can be specified, but it will be still ignored?
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:
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.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.