C++ 中的递归 class 模板和隐式实例化错误

Recursive class template and implicit instantiation error in C++

以下最小可重现示例包含模板 struct B,默认参数类型包含 lambda A<[]{ return 1; }>B 递归继承自 B<>。任何 A<z>.

都有 B 的特化
template<auto>
struct A{};

template<class = A<[]{ return 1; }>>
struct B : B<> {};

template<auto z>
struct B<A<z>> {};

B<int> x;

GCC 可以接受这个例子,Clang 抱怨说:

error: implicit instantiation of template 'B<A<{}>>' within its own definition

演示:https://gcc.godbolt.org/z/xzjzo7dE9

这里是哪个编译器?

P.S。如果这样修改 B 的定义:

template<class>
struct B : B<A<[]{ return 1; }>> {};

然后所有编译器都变得非常开心,演示:https://gcc.godbolt.org/z/roqnc39eq

Which compiler is right here?

两者都是,因为 B 格式错误; NDR。一如既往,[temp.res]/8 适用:

The validity of a template may be checked prior to any instantiation. The program is ill-formed, no diagnostic required, if:

(8.4) - a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or

模板是B,这里的“它的”定义是指主要的。如果我们在之后立即实例化 B<>,我们将得到一个无条件格式错误的 class 定义。因此,GCC 和 Clang 可以为所欲为。

顺便说一句,这整个 lambda 作为模板参数业务是一个转移注意力的问题。我们可以使用普通的、旧的 int 作为参数,也可以得到类似的差异。

template<int>
struct A{};

template<class = A<0>>
struct B : B<> {};

template<int z>
struct B<A<z>> {};

B<int> x;

int main() {}

Clang 仍然抱怨,但现在 so does GCC

是的,由于https://timsong-cpp.github.io/cppwp/n4868/temp.res#general-8.4,原始程序格式错误。

可以通过向前移动模板专业化来修复它:

template<auto>
struct A{};

template<class = A<[]{ return 1; }>>
struct B;

template<auto z>
struct B<A<z>> {};

template<class>
struct B : B<> {};

B<int> x;

现在被 Clang 和 GCC 接受:https://gcc.godbolt.org/z/6TYdvTnMa