带有对象的尾递归
Tail-recursion with objects
我有一个递归函数,我想让它成为尾递归函数。我的实际问题更复杂且依赖于上下文。但是我想解决的问题通过这个简单的程序演示:
#include <iostream>
struct obj
{
int n;
operator int&() { return n; }
};
int tail(obj n)
{
return tail(obj{ n + 1 > 1000 ? n - 1000 : n + 1 });
}
int main()
{
tail(obj{ 1 });
}
这似乎是很自然的尾递归。但事实并非如此,因为每次都必须调用obj n
的析构函数。至少 MSVC13 (编辑:)MSVC15 不会对此进行优化。如果我用 int 替换 obj
并相应地更改调用,它会按预期变为尾递归。
我的实际问题是:除了用int
替换obj
之外,有没有一种简单的方法可以使这种尾递归?我的目标是性能优势,因此使用堆分配的内存和new
很可能没有帮助。
简短回答:否。
更长的答案:你可能会找到一种方法来实现这一目标,但肯定不容易。由于标准不需要尾部调用优化,因此您永远无法确定对程序的一些微小更改是否会导致编译器无法优化代码。
更糟糕的是,考虑一下当您需要调试程序时会发生什么。编译器几乎肯定不会使用调试器标志优化高级尾部调用,这意味着您的程序只能在发布模式下正常工作。这将使程序更难维护。
尾递归的替代方法只需编写一个循环。它总是可以做到的,而且可能要复杂得多。它也不使用堆,因此开销会小得多。
由于您使用临时对象,因此我假设您在递归调用后不需要该对象。
一个相当黑客的解决方案是分配一个对象,传递一个指向它的指针,并在进行递归调用之前重新分配它,您将新构建的对象传递给递归调用。
struct obj
{
int n;
operator int&() { return n; }
};
int tail_impl(obj*& n)
{
int n1 = *n + 1 > 1000 ? *n - 1000 : *n + 1;
delete n;
n = new obj{n1};
return tail_impl(n);
}
int tail(obj n)
{
obj *n1 = new obj{n};
auto ret = tail_impl(n1);
delete n1;
return ret;
}
int main()
{
tail(obj{ 1 });
}
我显然省略了一些关键的异常安全细节。然而,GCC能够将tail_impl
变成一个循环,因为它确实是尾递归。
相关文章:
- 在对象指针上调用 Delete 是否会递归删除其动态分配的成员
- 将公共递归转换为尾递归,因为大型输入的堆栈溢出
- 如何在C++中表示JSON文档的递归对象结构?
- C++删除/(递归)对象销毁问题
- 尾递归函数未被 g++ 优化
- 尾递归,带有通过引用传递的向量
- 如何使O(n)的函数检查字母(上和下)和()+-*/到尾递归?
- 带有对象的尾递归
- 提升递归对象的序列化
- 在turbo c++中,可以将一个普通递归函数转换为尾递归函数来对其进行优化
- 为什么当递归函数结果相乘时,g++ 仍然优化尾递归
- 在 C++ 中使用尾递归函数计算列表的总和
- 叮当无限尾递归优化
- constexpr函数求值可以做尾递归优化吗?
- 尾递归函数,用于计算数组中大于平均值的所有数字
- 将递归函数转换为尾递归函数
- 为什么下面的函数没有使用 Clang 生成尾递归?
- 已知的最有效的尾递归素数验证函数是什么?
- 在c++中做尾递归
- 模板元编程的尾递归性能