类模板的部分专业化:编译器错误

Vla*_*cow 1 c++ templates class partial-specialization c++17

这个程序

#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;
}
Run Code Online (Sandbox Code Playgroud)

既不由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>
      |        ^~~~~~~~
Run Code Online (Sandbox Code Playgroud)

是否存在错误的类模板局部专业化?

Sto*_*ica 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
Run Code Online (Sandbox Code Playgroud)

然后,我们执行功能模块的重载解析和部分排序。如果#2获胜,那就更加专业了,您的声明是合法的。如果不赢,则声明无效。通过整理一些参数并针对一个模板对另一个模板进行模板参数推导来完成部分排序。因此,假设我们首先比较第一个和第二个(为了简单起见,我将它们重命名,但是它们仍然是重载的):

void foo1(A<0>); -> void foo2(A<I + 5>);
Run Code Online (Sandbox Code Playgroud)

论证推论在这里成功吗?没有。I + 5是非推论上下文:

[temp.deduct.type]

非推论上下文是:

5.3-非类型模板参数或数组绑定,其中子表达式引用模板参数。

I参考模板参数,所以I + 5是一种非推导上下文。因此,模板自变量推导在该方向上失败。

让我们尝试另一个方向。再次,我们提出一个论点

void foo2(A<1 + 5>); = void foo2(A<6>);  -> void foo1(A<I>); 
Run Code Online (Sandbox Code Playgroud)

推论显然在这里成功了。因此,根据功能模板部分排序的规则,foo1比更为专业foo2。这意味着我们的小学专业确实比我们的“部分专业化”更为专业,这使得部分专业化的形式不正确。