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

时间:2017-12-25 22:03:30

标签: c++ memory-management metaprogramming compiler-optimization compile-time

我最近一直在使用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 ++如何处理这些递归定义? (资源明智)

或更具体地说:

  • 编译器如何处理包含枚举值的模板类的实例创建(分配内存的位置/方式)?

  • 编译后生成的所有类是保留在内存中还是由编译器优化而只保留顶级类(清理)?

  • 是否存在与RAM无关的最大递归深度(编译器本身带来的限制)?

对此类结构的标准编制的深入解释将受到高度赞赏。

1 个答案:

答案 0 :(得分:2)

pow::value是编译时的常量表达式。编译器将通过计算pow<T, A, N>::value来计算A * pow<T, A, N - 1>::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]