c++templatesgccautonon-type-template-parameter

C++ auto template parameter deduction not working in GCC 14 unlike its older versions and Clang


The following snippet is accepted by GCC 13 and older, as well as all versions of Clang, but not by GCC 14. Either of the two suggested edits make GCC 14 accept the code as well.

#include <iostream>

template <int T> struct A { int value = T; };

// Replacing 'unsigned char' with 'int' makes it work with GCC 14:
template <unsigned char X> using B = A<X>;

// Or, replacing 'auto' with 'int' makes it work with GCC 14:
template <auto X>
void foo(B<X>& mat) noexcept
{
    std::cout << mat.value << "\n";
}

int main()
{
    A<2> mat;
    foo(mat);
}

The error emitted by GCC 14 is as follows, saying that the auto template parameter cannot be deduced unless auto is replaced with a specific type or the non-type template parameter of the alias has the same type as that of the aliased template:

<source>: In function 'int main()':
<source>:18:8: error: no matching function for call to 'foo(A<2>&)'
   18 |     foo(mat);
      |     ~~~^~~~~
<source>:10:6: note: candidate: 'template<auto X> void foo(B<((unsigned char)X)>&)'
   10 | void foo(B<X>& mat) noexcept
      |      ^~~
<source>:10:6: note:   template argument deduction/substitution failed:
<source>:18:8: note:   couldn't deduce template parameter 'X'
   18 |     foo(mat);
      |     ~~~^~~~~

Which compiler is correct?


Solution

  • It should compile. Paragraph 13.4.3/2 describes argument deduction:

    The value of a non-type template-parameter P of (possibly deduced) type T is determined from its template argument A as follows. If T is not a class type and A is not a braced-init-list, A shall be a converted constant expression (7.7) of type T; the value of P is A (as converted)

    The process of conversion is described in 7.7/13:

    A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only <skipped...>

    integral conversions (7.3.9) other than narrowing conversions (9.4.5),

    The description of narrowing conversions we find in 9.4.5/7, which also details which conversions are excluded from this:

    A narrowing conversion is an implicit conversion ... (7.4) — from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where (7.4.1) — the source is a bit-field whose width w is less than that of its type (or, for an enumeration type, its underlying type) and the target type can represent all the values of a hypothetical extended integer type with width w and with the same signedness as the original type or (7.4.2) — the source is a constant expression whose value after integral promotions will fit into the target type

    The last bullet describes the current case, integer value 2 fits the target type unsigned char, so it's excluded from narrowing conversion restrictions.