【问题标题】:How C++ handles recursive class definitions?C++ 如何处理递归类定义?
【发布时间】:2018-06-06 22:12:48
【问题描述】:

我最近开始使用 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 的最大递归深度(编译器本身的限制)?

非常感谢您对此类结构的标准编译进行深入解释。

【问题讨论】:

  • “是否存在最大递归深度...”:g++ 有-ftemplate-depth=n 参见:gcc.gnu.org/onlinedocs/gcc/…
  • 那么现在是 1024。但它是编译器约束还是只是一个约定(因为 17 对于以前的 C++ 标准来说是一个奇怪的选择)?
  • 我无法想象编译器会转储实例化类型,毕竟它无法知道它是否会在将来的某个时间再次使用。只有在链接阶段才会执行某种剔除。通过元编程,我希望剔除是完整的。

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


【解决方案1】:

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

...

计算pow&lt;T, A, N - n&gt;::value

计算pow&lt;T, A, N - n - 1&gt;::value

...

直到它发现不需要计算pow&lt;T, A, 1&gt;::value时停止,因为程序已经将N = 1的情况的值定义为pow&lt;T, A, 1&gt;::value = 1

如果有人会写:

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

GCC 会提醒

fatal error: template instantiation depth exceeds maximum of xxx

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

编译后,编译器只会将pow&lt;T, A, N - n&gt;::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&lt;int, 2, 8&gt;::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]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-11
    • 2018-01-23
    • 2014-05-23
    • 1970-01-01
    • 2017-07-26
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多