Следующий фрагмент принимается GCC 13 и старше, а также всеми версиями Clang, но не GCC 14. Любое из двух предложенных изменений заставляет GCC 14 также принимать код.
#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);
}
Ошибка, выдаваемая GCC 14, заключается в следующем: параметр шаблона auto
не может быть выведен, если auto
не заменен на определенный тип или параметр шаблона, не относящийся к типу, псевдонима имеет тот же тип, что и шаблон с псевдонимом:
<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);
| ~~~^~~~~
Какой компилятор правильный?
@PepijnKramer Спасибо! Можно, пожалуйста, ссылку на спецификацию?
Например, см. этот ответ: Неявное преобразование типов с шаблонными параметрами функции (но для всех шаблонов применяются одни и те же правила)
Я склонен думать, что gcc неверен, потому что template<auto X>
следует вывести как template<int X>
, и в этот момент проблем нет: godbolt.org/z/aGeaK4aG3
Он должен скомпилироваться. Параграф 13.4.3/2 описывает вывод аргументов:
Значение нетипового параметра шаблона P (возможно, выведенное) тип T определяется на основе аргумента шаблона A следующим образом. Если Т не тип класса и A не является списком инициализации в скобках, A должен быть преобразованное постоянное выражение (7.7) типа Т; значение P равно A (так как преобразовано)
Процесс конвертации описан в 7.7/13:
Преобразованное константное выражение типа T является выражением, неявно преобразуется в тип T, где преобразованное выражение является константой выражение, а последовательность неявного преобразования содержит только <skiped...>
интегральные преобразования (7.3.9), кроме сужающих преобразований (9.4.5),
Описание сужающих конверсий мы находим в 9.4.5/7, где также подробно описано, какие конверсии из этого исключены:
Сужающее преобразование — это неявное преобразование... (7.4) — от целочисленного типа или типа перечисления без области видимости к целочисленному типу, который не может представлять все значения исходного типа, за исключением случаев, когда (7.4.1) — источником является битовое поле, ширина которого w меньше ширины его тип (или, для типа перечисления, его базовый тип) и целевой тип может представлять все значения гипотетического расширенного целочисленный тип шириной w и с той же подписью, что и оригинал тип или (7.4.2) — источником является постоянное выражение, значение которого после комплексного продвижения будет соответствовать целевому типу
Последний маркер описывает текущий случай: целочисленное значение 2 соответствует целевому типу unsigned char
, поэтому оно исключено из сужающих ограничений преобразования.
gcc прав, отклонив его. Неявное преобразование типов не следует использовать при распознавании типов шаблонов.