【发布时间】:2013-06-04 15:38:41
【问题描述】:
在一本教科书中,我读到内联函数不能包含递归、转到、循环等。为什么? 如果我这样做,我认为它不会产生语法错误。
【问题讨论】:
-
那是哪本书?
inline仅与单一定义规则有关,它对您可以在函数中使用哪些功能没有影响。 -
你不是把它和 constexpr 搞混了吗?在 C++14 上架之前,这些目前确实在很多方面都受到限制。
在一本教科书中,我读到内联函数不能包含递归、转到、循环等。为什么? 如果我这样做,我认为它不会产生语法错误。
【问题讨论】:
inline 仅与单一定义规则有关,它对您可以在函数中使用哪些功能没有影响。
你的课本错了。
首先,重要的是要了解“将函数声明为inline”的概念与实际的“函数调用的内联”之间的区别。前者适用于函数本身,而后者独立适用于每个调用。
现在,内联函数可以包含任何东西。几乎所有对任何函数的直接调用都可以内联。没有任何限制。
显然,包含循环或本地 goto 的内联代码没有问题。因此,当您的书说内联函数“不能包含循环或 goto”时,它一定是指某些特定(并且可能非常古老)编译器的怪癖,由于某些特定于实现的原因,它无法内联此类函数。
递归是另一回事。显然,如果在编译时不知道递归深度,则不可能完全内联 所有 嵌套递归调用。然而,仍然可以将递归“解包”到特定的有限深度。 IE。编译器可以内联递归调用,例如 5 级深度,然后继续进行真正的(非内联)递归调用。这与称为“循环展开”的优化技术没有太大区别。因此,即使是递归函数也可以内联。一些编译器甚至可以让您控制递归函数的内联扩展深度。
【讨论】:
这段代码完全有效:
class Test
{
public:
inline void sayHello(int i);
};
void Test::sayHello(int i)
{
std::cout << "Hello "<< i << std::endl;
if (i>0)
sayHello(i-1);
}
int main(int argc, char** argv) {
Test t;
t.sayHello(10);
}
.. 并按预期递归。
(GCC 4.6.3)
在这种情况下,函数没有内联,因为内联只是对编译器的提示。不是要求。
作为上述cmets的建议者,你会在这里找到更完整的答案:Can a recursive function be inline?
【讨论】:
这听起来像是 C++ 内联函数的概念与扩展内联函数调用的优化之间的混淆。
内联函数是在每个使用它的翻译单元中定义的函数;与非内联函数不同,非内联函数必须根据单一定义规则仅在一个翻译单元中定义。这些函数可以做什么没有特别的限制;特别是,他们可以像你说的那样递归地调用自己。
内联函数和优化之间的联系是许多编译器在编译调用站点时需要定义可用;这通常需要内联函数才能在多个翻译单元中定义。
这本书的意思可能是说递归调用不能内联扩展,因为这意味着生成的代码必须包含自身的副本;那是不可能的。但是仍然没有理由不包含循环、分支或任何其他正常的流控制语句。
【讨论】:
正如 Mike 所说,语言定义对 inline 函数没有这样的限制。您有时会看到一个编译器警告,该函数未内联扩展,因为它使用了一些编译器不会内联的语言结构,例如递归、循环等。这仅告诉您该函数没有内联扩展,因此您没有得到您可能想要的优化。代码有效,生成的可执行文件将正常工作。
【讨论】:
我们可以在内联函数中使用循环、递归等。但为了良好的编程习惯,我们不这样做;因为对于循环中编写的指令的每一次执行,我都必须由您的 CPU 管理。它可能会占用您内存中的一些额外空间。
【讨论】: