template<template<auto> class> struct A {};
template<int&> struct B {};

A<B> a;

int main() {}

All three compilers MSVC, GCC and Clang in their latest versions accept this code (https://godbolt.org/z/b7Pv7Ybxv). However, auto cannot deduce to a reference type, so why would this be allowed? Or are the compilers incorrectly accepting it when looking at what the standard says?


I confused the argument and parameter in the above. My intention was to ask about

template<template<int&> class> struct A {};
template<auto> struct B {};

A<B> a;

int main() {}

which is also accepted by all of the compilers above. The original example is however also interesting because the template template parameter should be at least as specialized as the template argument, which it doesn't intuitively seem to be.

1

There are 1 best solutions below

4
On

The second example is OK according to the rules. Let us quote them:

#3 A template-argument matches a template template-parameter P when P is at least as specialized as the template-argument A ...

#4A template template-parameter P is at least as specialized as a template template-argument A if, given the following rewrite to two function templates, the function template corresponding to P is at least as specialized as the function template corresponding to A according to the partial ordering rules for function templates. Given an invented class template X with the template-head of A (including default arguments and requires-clause, if any):

  • Each of the two function templates has the same template parameters and requires-clause (if any), respectively, as P or A.
  • Each function template has a single function parameter whose type is a specialization of X with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP in the template-head of the function template, a corresponding template argument AA is formed. If PP declares a template parameter pack, then AA is the pack expansion PP... ([temp.variadic]); otherwise, AA is the id-expression PP.

So our template-argument is template<auto> class and our template parameter is template<int&> class. Let's invent a class template X:

 template <auto> class X {};

and rewrite as two function templates:

 // rewritten argument
 template<auto V> void func(X<V>);
 // rewritten parameter
 template<int& V> void func(X<V>);

The second one seems to be at least as specialized as the first: every function call that would be satisfied by the second template would be also satisfied by the first one. (As far as I can tell the second template can never be satisfied because you cannot instantiate X with an int& argument; not sure if this is IFNDR; probably not because the program per se does not contain func templates, they are invented in order to check other templates).


Less obviously, the first example is also OK according to the same rules. Lety's invent class template X and rewrite again:

 template <int&> class X {};

 // rewritten argument
 template<int& V> void func(X<V>);
 // rewritten parameter
 template<auto V> void func(X<V>);

Now the two templates satisfy exactly the same calls, so each one is at least specialized as the other one.