Vittorio Romeo Vittorio Romeo - 10 months ago 138
C++ Question

Can C++17's deduded `auto` non-type `template` parameters pattern-match templates with explicit non-type parameters?

Consider this example (also available on wandbox):

template <template <auto> class>
void test() { }

template <int>
struct X { };

Trying to instantiate
4.0 (trunk) results in a compilation error:

error: no matching function for call to 'test'

note: candidate template ignored:
invalid explicitly-specified argument for 1st template parameter
void test() { }

My initial assumption/intuition was that
could be used to match any
having a non-type parameter.

However, the following code snippet successfully compiles:

template <template <auto> class>
void test() { }

// vvvv
template <auto>
struct X { };

Is this intended? Could not find anything conclusive in P0127R2.

Answer Source

It's definitely intended. Template-template parameters can only match templates which take the same kinds of arguments. This:

template <template <auto> class>
void test() { }

can only be instantiated with a class template that can take any kind of non-type parameter. But this:

template <int> 
struct X { };

is not such a class template. X can only be instantiated with an int. It simply does not match the specification for the template template parameter, hence the error. What if test wanted to instantiate its class template with a pointer type? Or pointer to function or pointer to member? That would be impossible.

Your second attempt, with template <auto> struct X { }; does match the template-template parameter, hence is well-formed. Note also that the reverse, having test take a template <int> class parameter and passing in template <auto> struct X { }; is also well-formed as the argument is more general than the parameter.

The relevant wording is in [temp.arg.template]:

A template-argument matches a template template-parameter P when each of the template parameters in the template-parameter-list of the template-argument’s corresponding class template or alias template A matches the corresponding template parameter in the template-parameter-list of P. Two template parameters match if they are of the same kind (type, non-type, template), for non-type template-parameters, their types are equivalent (, and for template template-parameters, each of their corresponding template-parameters matches, recursively.

Note: the equivalence wording accepts the auto - auto case and rejects the auto - int case, but also seems to reject the int - auto case (based on my reading). I'm going to try to get some clarification on it.