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++ 如何处理那些递归定义? (资源方面)
或者更具体地说:
编译器如何处理包含枚举值的模板classes的实例创建(where/how是分配的内存)?
编译后生成的所有 classes 是否都保留在内存中,或者它们是否被编译器优化掉,只保留顶部 class(清理)?
是否有独立于 RAM 的最大递归深度(编译器本身造成的限制)?
非常感谢有关此类结构的标准编译的深入解释。
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=]x100
是pow<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]
我最近开始使用 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++ 如何处理那些递归定义? (资源方面)
或者更具体地说:
编译器如何处理包含枚举值的模板classes的实例创建(where/how是分配的内存)?
编译后生成的所有 classes 是否都保留在内存中,或者它们是否被编译器优化掉,只保留顶部 class(清理)?
是否有独立于 RAM 的最大递归深度(编译器本身造成的限制)?
非常感谢有关此类结构的标准编译的深入解释。
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=]x100
是pow<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]