执行生成运行时代码的递归模板
do recursive templates producing runtime code?
考虑以下类:
class test {
// recursively template
template<typename T, typename... R>
void add(T t, R... r) {
// do something with t
if(sizeof...(r))
add(r...);
}
// since the variadic template add is recursive, there have to be an end.
void add() {}
public:
template<typename... T>
explicit test(T... rest) {
add(rest...);
}
};
和以下主要:
int main() {
test t1(1);
test t2(1, 2);
test t3(1, 2, 3);
}
我缩小了代码,因此可能不需要add
方法。
我认为这段代码不是在生成运行时递归代码,而是创建 3 个具有 3 个不同数量参数的不同构造函数。 我说的对吗? 我只是想确定我是否正确。 如果不是,那会发生什么?
编辑:
Bames53 和卡西欧 Neri 的答案正是我所期望的正在发生的事情。 但是,它不是递归的,但它仍然调用单独的 add,就像您在 Bames53 答案中看到的那样。 这就像半递归。
模板中的所有代码都是在编译时生成的。这就是模板的全部意义所在,可变参数模板没有什么不同,通常你做编译时递归来获取可变参数模板终止。基本上就像您编写了嵌套的方法一样。在扩展模板的编译器阶段之后(不确定这是否正是它的工作原理,我不是编译器专家),它基本上看起来好像从未有过模板,它们被扩展并变成模板实例,本质上与普通代码没有什么不同。我猜通常编译器也会内联可变参数模板生成的大多数方法,以生成更高效的代码。
编辑:请记住,当我写这篇文章时,我决定给你一些荣誉,并假设你的实际代码比你发布的内容做得更多(基本上什么都不做)
是的,在您的情况下,编译器将生成 3 个重载的 test::add
和 3 个重载的test::test
(取 1、2 和 3 个类型 int
的参数)。
要检查这一点,请使用选项 -std=c++11 -c main.cpp
使用 gcc 编译代码(在文件 main.cpp
中)。这将产生main.o
.然后使用 nm -C main.o
检查对象文件中的符号。你会得到(除其他外)
00000000 T void test::add<int>(int)
00000000 T void test::add<int, int>(int, int)
00000000 T void test::add<int, int, int>(int, int, int)
00000000 T test::test<int>(int)
00000000 T test::test<int, int>(int, int)
00000000 T test::test<int, int, int>(int, int, int)
您可以在其中看到所有提到的重载。
值得一提的是,gcc 没有为不带参数test::add
创建代码(非模板函数),因为它内联了调用。如果将定义移出类:
void test::add() {}
然后 GCC 也生成这个符号,nm -C main.o
的输出包括
00000000 T test::add()
模板在编译时生成常规类和函数。生成的代码(如构造函数和函数)在运行时运行,就像普通代码一样。
你的程序本质上与你编写的程序相同:
class test {
void add(int t) {
if(0)
add();
}
void add(int t, int r) {
if(1)
add(r);
}
void add(int t, int r, int r2) {
if(2)
add(r, r2);
}
void add() {}
public:
explicit test(int a) { add(a); }
explicit test(int a, int b) { add(a, b); }
explicit test(int a, int b, int c) { add(a, b, c); }
};
int main() {
test t1(1);
test t2(1, 2);
test t3(1, 2, 3);
}
因此,实际上有"运行时"代码,就像您编写了这些普通函数一样,但模板"创建 3 个具有 3 个不同数量的参数的不同构造函数"也是事实。
模板通过计算要生成的代码来执行"编译时"计算。这可以被更大程度地利用,通常称为"模板元编程"。典型的无用示例:
template<int i>
struct fib {
enum { value = fib<i-1>::value + fib<i-2>::value };
};
template<> struct fib<0> { enum { value = 1 }; };
template<> struct fib<1> { enum { value = 1 }; };
int main() {
return fib<4>::value;
}
这基本上与我写的相同:
struct fib_0 { enum { value = 1 }; };
struct fib_1 { enum { value = 1 }; };
struct fib_2 { enum { value = 2 }; };
struct fib_3 { enum { value = 3 }; };
struct fib_4 { enum { value = 5 }; };
int main() {
return 5;
}
因此,模板只是生成普通代码。编译时计算是确定要生成的代码。
是的,将生成 3 个不同的test
实例,并且每个调用都会生成一堆 test::add
成员函数定义。但最终,你的代码什么都没做,所以它都会被优化掉。下面是启用了 -O3
的 g++4.8.1 的程序集输出。
.file "main.cpp"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB3:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE3:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1"
.section .note.GNU-stack,"",@progbits
我远不是阅读 x86 汇编的专家,但我认为很明显,您的所有代码都被最终的可执行文件抛弃了。
- 如何使用递归循环我的代码(当用户输入无效输入时,它会再次提示他们)?
- 理解递归代码的演练
- 我遇到了这个代码片段,不明白. 它递归检查 C++ 字符串中是否存在大写字符
- 为什么我的递归代码给出 sigsegv 错误?
- 以下递归代码的时间复杂度是多少?
- 弄清楚这段代码是怎么回事(递归下降案例研究)
- Fibbonaci 递归代码返回错误值,始终返回下一个数字
- 我的递归可以吗?是否有任何破坏代码的示例?
- 我的编辑距离递归代码中的字符类型有问题
- 该递归代码用于计算指数的运行时间是多少
- 递归代码快速排序
- C 递归代码迭代
- 如何确定递归代码的Big-O
- 这个递归代码有什么问题
- 跟踪此递归代码
- 以迭代方式编写递归代码
- 递归代码上的运行时错误
- 如何将递归代码更改为迭代形式
- 为什么我的递归代码会导致堆栈溢出错误
- 查找无序后继递归代码中的段错误