Is a template with reference non-type template parameter supposed to match a template template parameter with an auto non-type template parameter?
Asked Answered
S

1

7
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.

Speiss answered 23/12, 2022 at 0:17 Comment(2)
This is an intriguing example. I don't think it should be accepted even if int& is replaced by int. template <auto> is not at least as specialized as template <int>. It should be the other way around.Anticipant
@BrianBi That's true. I did intent to ask the other way around but got it mixed up. Exchanging auto and int& the compilers however also accept it. I think they basically consider an auto non-type parameter to match any other non-type parameter in either direction. But I don't see why.Speiss
O
0

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.

Obloquy answered 27/12, 2022 at 10:11 Comment(4)
It is not clear to me how these functions are ordered in partial ordering of function templates either. It is impossible to test for your first case but for the last snippet the compilers all disagree: MSVC considers them ambiguous (godbolt.org/z/hjG1sdT34), GCC considers the first overload more specialized (godbolt.org/z/1KeEPYGvP) and Clang considers the second overload non-viable for my test call (godbolt.org/z/MnTKKMved).Speiss
MSVC looks right to me according to the standard. Why clang considers the second overload non-viable is unclear. For the first overload to be more specialized, there should be some x such that for func(x) only the second overload is viable, but I cannot imagine such x.Obloquy
I think partial ordering should try template argument deduction similar to the following, for which compiler agree in almost all combinations on deduction failure: godbolt.org/z/rhjhK63cr, godbolt.org/z/hMdcrTM95, godbolt.org/z/eeErEqnW8, godbolt.org/z/d886YKKzz. By that measure all (except Clang) should agree that the first overload in your last snippet is more specialized, but they should consider the two overloads with template<auto> class X {}; incomparable in the partial order. Although I am not sure that "at least as specialized" is properly defined.Speiss
I'm now not sure how to interpret the partial ordering rules in this case, or whether they make sense at all.Obloquy

© 2022 - 2024 — McMap. All rights reserved.