C 如何处理递归类别的定义

How C++ handles recursive class definitions?

本文关键字:递归 定义 处理 何处理      更新时间:2023-10-16

我最近开始使用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   $0x100,-0x10(%rbp)
  ...
}

在这里,$0x100pow<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]