C++ 递归函数可以内联吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/190232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can a recursive function be inline?
提问by Ashwin
inline int factorial(int n)
{
if(!n) return 1;
else return n*factorial(n-1);
}
As I was reading this, found that the above code would lead to "infinite compilation" if not handled by compiler correctly.
当我阅读本文时,发现如果编译器处理不当,上述代码将导致“无限编译”。
How does the compiler decide whether to inline a function or not ?
编译器如何决定是否内联函数?
回答by Derek Park
First, the inline
specification on a function is just a hint. The compiler can (and often does) completely ignore the presence or absence of an inline
qualifier. With that said, a compiler caninline a recursive function, much as it can unroll an infinite loop. It simply has to place a limit on the level to which it will "unroll" the function.
首先,inline
函数的规范只是一个提示。编译器可以(并且经常这样做)完全忽略inline
限定符的存在与否。话虽如此,编译器可以内联递归函数,就像它可以展开无限循环一样。它只需要限制它将“展开”函数的级别。
An optimizing compiler might turn this code:
优化编译器可能会转换此代码:
inline int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
return factorial(x);
}
into this code:
进入这个代码:
int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
if (x <= 1)
{
return 1;
}
else
{
int x2 = x - 1;
if (x2 <= 1)
{
return x * 1;
}
else
{
int x3 = x2 - 1;
if (x3 <= 1)
{
return x * x2 * 1;
}
else
{
return x * x2 * x3 * factorial(x3 - 1);
}
}
}
}
In this case, we've basically inlined the function 3 times. Some compilers doperform this optimization. I recall MSVC++ having a setting to tune the level of inlining that would be performed on recursive functions (up to 20, I believe).
在这种情况下,我们基本上已经内联了该函数 3 次。一些编译器确实执行了这种优化。我记得 MSVC++ 有一个设置来调整将在递归函数上执行的内联级别(我相信最多 20 个)。
回答by Matt J
Indeed, if your compiler does not act intelligently, it may try inserting copies of your inline
d function recursively, creating infinitely-large code. Most modern compilers will recognize this, however. They can either:
事实上,如果您的编译器没有智能地运行,它可能会尝试inline
递归插入d 函数的副本,从而创建无限大的代码。然而,大多数现代编译器都会认识到这一点。他们可以:
- Not inline the function at all
- Inline it up to a certain depth, and if it hasn't terminated by then, call the separate instance of your function using the standard function calling convention. This can take care of many common cases in a high-performance manner, while leaving a fallback for the rare case with a large call depth. This also means that you keep both inlined and separate versions of that function's code around.
- 根本不内联函数
- 将它内联到一定深度,如果到那时它还没有终止,请使用标准函数调用约定调用函数的单独实例。这可以以高性能的方式处理许多常见情况,同时为调用深度较大的罕见情况留下后备。这也意味着您要保留该函数代码的内联版本和单独版本。
For case 2, many compilers have #pragma
s you can set to specify the maximum depth to which this should be done. In gcc, you can also pass this in from the command-line with --max-inline-insns-recursive
(see more info here).
对于情况 2,许多编译器都#pragma
可以设置 s 来指定应执行此操作的最大深度。在gcc 中,您还可以从命令行传递它--max-inline-insns-recursive
(在此处查看更多信息)。
回答by leppie
AFAIK GCC will do tail call elimination on recursive functions, if possible. Your function however is not tail recursive.
如果可能,AFAIK GCC 将对递归函数进行尾调用消除。但是,您的函数不是尾递归的。
回答by Paul Nathan
The compiler creates a call graph; when a cycle is detected calling itself, the function is no longer inlined after a certain depth(n=1, 10, 100, whatever the compiler is tuned to).
编译器创建一个调用图;当检测到循环调用自身时,该函数在一定深度后不再内联(n=1, 10, 100,无论编译器调整到什么)。
回答by alex strange
Some recursive functions can be transformed into loops, which effectively infinitely inlines them. I believe gcc can do this, but I don't know about other compilers.
一些递归函数可以转换为循环,从而有效地无限内联它们。我相信 gcc 可以做到这一点,但我不知道其他编译器。
回答by yungchin
See the answers already given for why this won't typically work.
请参阅已经给出的答案,了解为什么这通常不起作用。
As a "footnote", you could achieve the effect you're looking for (at least for the factorial you're using as an example) using template metaprogramming. Pasting from Wikipedia:
作为“脚注”,您可以使用模板元编程实现您正在寻找的效果(至少对于您用作示例的阶乘)。从维基百科粘贴:
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
回答by Windows programmer
"How does the compiler decide whether to inline a function or not ?"
“编译器如何决定是否内联函数?”
That depends on the compiler, the options that were specified, the version number of the compiler, maybe how much memory is available, etc.
这取决于编译器、指定的选项、编译器的版本号、可能有多少可用内存等。
The program's source code still has to obey the rules for inlined functions. Whether or not the function gets inlined, you have to prepare for the possibility that it will be inlined (some unknown number of times).
程序的源代码仍然必须遵守内联函数的规则。无论函数是否被内联,您都必须为它被内联的可能性做好准备(一些未知的次数)。
The Wikipedia statement that recursive macros are typically illegal looks rather poorly informed. C and C++ prevent recursive invocations but a translation unit doesn't become illegal by containing macro code that looks like it would have been recursive. In assemblers, recursive macros are typically legal.
维基百科关于递归宏通常是非法的声明看起来很不明智。C 和 C++ 防止递归调用,但翻译单元不会因为包含看起来像递归的宏代码而变得非法。在汇编程序中,递归宏通常是合法的。
回答by mattlant
The compiler will make a call graph to detect these sorts of things and prevent them. So it would see that the function calls itself and not inline.
编译器将制作一个调用图来检测这些类型的事情并阻止它们。所以它会看到函数调用自身而不是内联。
But mainly it is controlled by the inline keyword and compiler switches(For example, you can have it auto inline small functions even without the keyword.) Its important to note that Debug compilations should never be inlining as the callstack will not be preserved to mirror the calls you created in code.
但主要是由 inline 关键字和编译器开关控制(例如,即使没有关键字,您也可以让它自动内联小函数。)重要的是要注意 Debug 编译不应该内联,因为调用堆栈不会被保留以进行镜像您在代码中创建的调用。
回答by Roger Nelson
Some compilers (I.e. Borland C++) do not inline code that contains conditional statements (if, case, while etc..) so the recursive function in your example would not be inlined.
某些编译器(即 Borland C++)不会内联包含条件语句(if、case、while 等)的代码,因此不会内联示例中的递归函数。