C++ 如何处理递归 class 定义?

How C++ handles recursive class definitions?

我最近开始使用 C++ 进行模板元编程,并一直在尝试将一些基本函数转换为它们各自的递归编译时模板定义。

例如:

template <typename T, T A, unsigned int N>
class pow { enum : T { value = A * pow<T, A, N-1>::value } };
template <typename T, T A> class pow<T, A, 0> { enum : T { value = 1 } };

模板的语法和强大功能令我惊叹。然而,有一个问题困扰着我: C++ 如何处理那些递归定义? (资源方面)

或者更具体地说:

非常感谢有关此类结构的标准编译的深入解释。

pow::value是编译时的常量表达式。编译器会通过计算A * pow<T, A, N - 1>::value来计算pow<T, A, N>::value。字面量 A 在编译时也是一个 const 值,编译器会继续计算 pow<T, A, N - 1>::value

...

计算pow<T, A, N - n>::value

计算pow<T, A, N - n - 1>::value

...

直到发现不需要计算pow<T, A, 1>::value就停止,因为程序已经定义了N=1的情况下的值为pow<T, A, 1>::value = 1

如果有人会写:

int main() {
    int value = pow<int, 1, -1>::value;
}

GCC 会提醒

fatal error: template instantiation depth exceeds maximum of xxx

那是因为编译器在达到最大递归深度之前找不到要解析的常量值。

编译后,编译器只会将 pow<T, A, N - n>::value 的值保留为 immediate number,而不会存储编译期间解析的任何中间值。

int main() {
  400546:       55                      push   %rbp
  400547:       48 89 e5                mov    %rsp,%rbp
  ...
    int a = pow<int, 2, 8>::value;
  40055d:       c7 45 f0 00 01 00 00    movl   [=12=]x100,-0x10(%rbp)
  ...
}

这里,[=26=]x100pow<int, 2, 8>::value的结果。没有额外的地址保存这个结果。

递归深度的最大值由编译器指定。默认最大值为 900。可以使用 GCC 中的 -ftemplate-depth 开关设置此值。

但是,-ftemplate-depth 值不能超过 32 位整数的最大值。

在上面的例子中,递归深度也可能受限于type T

int main() {
    int result = pow<int, 2, 200>::value;
}

GCC 会提醒

error: overflow in constant expression [-fpermissive]