该程序
#include <iostream>
template <int I>
struct A
{
A() { std::cout << "A<I>()\n"; }
};
template <int I>
struct A<I + 5>
{
A() { std::cout << "A<I + 5>()\n"; }
};
int main()
{
return 0;
}
既不是由gcc HEAD 10.0.0 20190也不是由clang HEAD 10.0.0编译的。
例如gcc编译器发出错误
prog.cc:10:8: error: template argument '(I + 5)' involves template parameter(s)
10 | struct A<I + 5>
| ^~~~~~~~
是否存在错误的类模板局部专业化?
答案 0 :(得分:6)
这不是有效的部分专业化(尽管错误可能会更好)。我们不内联的子句如下:
[temp.class.spec]
8在类模板的部分参数列表中 专业化,以下限制适用:
- 专业化应该比主要模板更加专业化。
“专业不是更专业!?”我想你想。但是确实是这样。 [temp.class.order]中描述了确定“更专业”的规则。要点是我们要考虑两个假设的函数模板重载:
template <int I>
struct A { /* ... */ };
template<int I>
void foo(A<I>); //1
template<int I>
void foo(A<I + 5>); //2
然后我们执行功能模块的重载解析和部分排序。如果#2获胜,那就更加专业了,您的声明是合法的。如果不赢,则声明无效。通过整理一些参数并针对一个模板对另一个模板进行模板参数推导来完成部分排序。因此,假设我们首先比较第一个和第二个(为了简单起见,我将它们重命名,但是它们仍然是重载的):
void foo1(A<0>); -> void foo2(A<I + 5>);
论证推论在这里成功吗?没有。 I + 5
是非推论上下文:
[温度扣除类型]
非推论上下文是:
5.3-非类型模板参数或数组绑定,其中 子表达式引用模板参数。
I
引用了模板参数,因此I + 5
是一个非推论上下文。因此,模板参数推导在该方向上失败。
让我们尝试另一个方向。再次我们吵架
void foo2(A<1 + 5>); = void foo2(A<6>); -> void foo1(A<I>);
推论显然在这里成功了。因此,根据功能模板部分排序的规则,foo1
比foo2
更专业。这意味着我们的小学专业确实比我们的“部分专业化”专业化,这使得部分专业化的形式不正确。
答案 1 :(得分:1)
如果由于 template-parameter-list 和 template-id 的结构而无法推导部分专业化的模板参数,则该程序不适用于形成。
以示例
template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {}; // error
template <int I> struct A<I, I> {}; // OK
template <int I, int J, int K> struct B {};
template <int I> struct B<I, I*2, 2> {}; // OK
Clang的错误消息与此相呼应:
error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
(请参阅 @StoryTeller 的答案,以了解为什么这种推论在您的代码中失败了,在此我将不再重复。)