以下代码片段被 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);
| ~~~^~~~~
哪个编译器是正确的?
它应该可以编译。 第 13.4.3/2 段描述了参数推导:
(可能推导出来的)非类型模板参数 P 的值 类型 T 由其模板参数 A 确定,如下所示。如果 T 是 不是类类型并且 A 不是花括号初始化列表,A 应是 转换后的 T 类型常量表达式 (7.7); P 的值为 A(如 转换)
转换的过程在7.7/13中有描述:
T 类型的转换常量表达式是一个隐式表达式 转换为 T 类型,其中转换后的表达式是常量 表达式和隐式转换序列仅包含
除缩小转换 (9.4.5) 之外的积分转换 (7.3.9),
我们在9.4.5/7中找到了缩小转换的描述,其中还详细说明了哪些转换被排除在外:
缩小转换是隐式转换... (7.4) — 从整数类型或无范围枚举类型到整数类型 无法表示原始类型的所有值,除了,其中 (7.4.1) — 源是一个位域,其宽度 w 小于 它的类型(或者,对于枚举类型,其基础类型)和 目标类型可以表示假设扩展的所有值 宽度为 w 且与原始符号具有相同符号的整数类型 type or (7.4.2) — 源是一个常量表达式,其值 积分促销后会符合目标类型
最后一个项目描述了当前情况,整数值 2 适合目标类型
unsigned char
,因此它被排除在缩小转换限制之外。