Open Liam0205 opened 5 years ago
太棒了!感谢分享对递归的理解。
但是: 什么是递归呢?
我觉得是函数在定义的时候,包含自己。再外加一个终止条件。
这个是函数定义:
template <int N>
void counting(std::ostream& os) {
counting<N - 1>(os); // *
os << N << std::endl;
}
终止条件:
template <>
void counting<1>(std::ostream& os) {
os << 1 << std::endl;
}
从远看起来特别像是递归函数。
归实例化的深度是有限制
这个限制是由于物理内存大小的限制么?为什么是 256 层? 用 x64 编译器会好一些么?
@randomwangran
递归(英语:Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法重复事物的过程。
C++ 里,由不同的模板实参实例化的函数模板是不同函数。所以在这里不是递归。但你讲的没错,它的形式看起来和函数递归很相似。所以,它在编译的时候,编译器会「递归地实例化」;不过在执行期只是若干不同函数的调用,不会产生递归。
这里递归实例化的深度限制依次来自寻址空间的限制(32bit or 64bit)、可用内存的限制、编译器内某些数据结构的大小限制。你说的两个因素会限制递归实例化的深度,但在这里不是直接原因。直接原因是「编译器就这么设置了一个值」。clang
预设了 256,gcc
预设了 900。就这么直白。当然这个预设值是可以改的;不过这里就不展开讲了。
博主你好,非常感谢你的博文。
根据汇编的方式进行理解可能效果更好?
经过反汇编你的代码,可以发现很多命名为*countint*
的函数,
在目标文件层面,可以确认不是递归操作。
@horqin 博主你好,非常感谢你的博文。
根据汇编的方式进行理解可能效果更好?
经过反汇编你的代码,可以发现很多命名为
*countint*
的函数, 在目标文件层面,可以确认不是递归操作。
从汇编理解确实是个办法。不过这不需要反汇编呀。你有源代码了,直接编译到汇编码停止就好了。例如这个网站所示:
@Liam0205
@horqin 博主你好,非常感谢你的博文。
根据汇编的方式进行理解可能效果更好?
经过反汇编你的代码,可以发现很多命名为
*countint*
的函数, 在目标文件层面,可以确认不是递归操作。从汇编理解确实是个办法。不过这不需要反汇编呀。你有源代码了,直接编译到汇编码停止就好了。例如这个网站所示:
gcc 与 clang 编译器缺乏 无用标签过滤 操作,
直接生成的汇编代码通常存在很多冗余信息,即存在大量标签。
因此通常需要使用 objdump -d foo.o
命令间接生成可读性更佳的汇编代码。
博主提供的软件 Compiler Explorer,也正在不断改进 无用标签过滤 功能。
因此,反汇编其实正是“编译”与“无用标签过滤”的组合罢了。
@Liam0205
@horqin 博主你好,非常感谢你的博文。 根据汇编的方式进行理解可能效果更好? 经过反汇编你的代码,可以发现很多命名为
*countint*
的函数, 在目标文件层面,可以确认不是递归操作。从汇编理解确实是个办法。不过这不需要反汇编呀。你有源代码了,直接编译到汇编码停止就好了。例如这个网站所示: https://gcc.godbolt.org/z/yK0PIV
gcc 与 clang 编译器缺乏 无用标签过滤 操作, 直接生成的汇编代码通常存在很多冗余信息,即存在大量标签。 因此通常需要使用
objdump -d foo.o
命令间接生成可读性更佳的汇编代码。博主提供的软件 Compiler Explorer,也正在不断改进 无用标签过滤 功能。
因此,反汇编其实正是“编译”与“无用标签过滤”的组合罢了。
你讲的对。
https://liam.page/2019/02/19/recursive-like-function-template-calling/
遇到一个蛮奇葩的问题:在 C++ 中如何不用循环和递归,打印从 1 至 N 的自然数? 想了想,用模板元编程可以解决——让编译器在编译期把该打印的东西都展开就好了。