3

我在我的文档中读到了关于内联函数的内容。我的文件说:有两种类型的内联函数:implicity functionexplicity function.

Explicity function:您inline在函数之前使用关键字,并在类之外使用。例如:

inline int Math::add(int a, int b){ return a + b; }

Implicity function:类中的每个方法都是隐式的。例如:

class Math {
   int add(int a, int b) { return a + b;}   // implicity inline function
};

所以,如果这是真的,那么,我不想使用的每个方法,我都inline必须在类之外声明,对吗?如果这是真的,我可以在类中实现一个方法并且不想要内联函数。

谢谢 :)

4

4 回答 4

7

保证它不是内联的唯一方法是使其在编译时无法访问,例如,将其主体定义放入 cpp 文件而不是头文件。

更新:一位评论者说,即使将函数体放入不同的编译单元也不能保证有帮助。他是完全正确的。通常它会有所帮助,但一些编译器仍然可能内联该函数。因此,没有可靠的方法来禁用不依赖于编译器的内联。

所有的内联只是优化的问题。如果启用了适当的优化,通过编写一个 inline 关键字,您只需告诉编译器您建议内联该函数。您既不能强制编译器内联函数,也不能强制编译器不内联它。对于某些编译器,例如 VC++,有一些方法可以做到这一点(__declspec(noinline)),但它们都依赖于编译器。

为什么需要禁用内联?编译器通常知道得更好......如果是出于调试目的,只需禁用优化,或者至少禁用函数内联。您甚至可以在单个文件中使用编译指示来执行此操作。无论如何,通常应该避免调试发布版本,尽管有时当然是无法避免的。

于 2012-11-14T08:48:19.313 回答
5

阅读C++ 标准,如何定义“隐式内联函数”:

成员函数可以在其类定义中定义(8.4),在这种情况下,它是一个内联成员函数(7.1.2),或者如果它已经被声明但未在其类定义中定义,则它可以在其类定义之外定义类定义。出现在类定义之外的成员函数定义应出现在包含类定义的命名空间范围内。除了出现在类定义之外的成员函数定义,以及出现在类定义之外的类模板和成员函数模板 (14.7) 的成员函数的显式特化之外,不得重新声明成员函数。

编译器也不保证它真的会执行替换:

带有 inline 说明符的函数声明(8.3.5、9.3、11.4)声明了一个内联函数。inline 说明符向实现表明,在调用点对函数体进行内联替换优于通常的函数调用机制。 在调用点执行此内联替换不需要实现;然而,即使省略了这个内联替换,7.1.2 中定义的内联函数的其他规则仍应得到遵守。

尽管打开优化后可能会发生许多奇怪的事情,但其他所有功能都可能是“非内联”的,例如查看gcc- 优化选项

  • -finline-small-functions 作为一部分O2

    当函数体小于预期的函数调用代码时,将函数集成到调用者中(因此程序的整体大小变得更小)。编译器启发式地决定哪些函数足够简单,值得以这种方式集成。这种内联适用于所有函数,甚至那些未声明为内联的函数。

  • -finline-functions 作为一部分O3

    考虑所有的内联函数,即使它们没有被声明为内联。编译器启发式地决定哪些函数值得以这种方式集成。
    如果对给定函数的所有调用都被集成,并且该函数被声明为静态,那么该函数通常不会作为汇编代码本身输出。

  • -finline-functions-called-once 作为一部分O1

    考虑所有调用一次的静态函数以内联到它们的调用者,即使它们没有标记为内联。如果集成了对给定函数的调用,则该函数本身不会作为汇编代码输出。

另一方面,您可以告诉编译器不要内联函数 ( -fno-inline):

除了用 always_inline 属性标记的函数外,不要展开任何内联函数。这是不优化时的默认值。
单个函数可以通过用 noinline 属性标记来免除内联。

于 2012-11-14T08:58:07.493 回答
3

是的,这是真的,在类定义中定义的所有方法都是隐式inline

请注意,inline这并不意味着编译器实际上会在代码中内联它。如果您不希望它是内联的,只需在实现文件中分离实现即可。

于 2012-11-14T08:38:38.960 回答
2

inline首先,对类中定义的函数使用关键字是完全合法的:

struct MyClass
{
    inline int someFunctions() { return 42; }
};

这里的关键字是多余的,但不是非法的。

其次,虽然inline关键字是对编译器的提示,但它唯一正式的、必需的含义是允许函数的多个定义,而不会由于违反一个定义规则而导致未定义的行为。编译器在某些情况下会忽略它:

  • 当为调试设计的选项被提供(或优化被关闭)时,大多数编译器会忽略它,并且实际上不会内联任何东西。

  • 当打开最大优化时,最好的编译器会完全忽略它;函数是否内联将唯一取决于编译器对代码和分析数据的分析,并且给定函数将内联在一个位置(它位于紧密循环的中间),而不是在另一个位置。(与其他一些发帖人所说的不同,即使调用站点和函数定义位于两个不同的翻译单元中,也会发生这种情况。)

在这两个极端之间,许多编译器进行模块间分析,并且至少在开启某种程度的优化时会“接受提示”,至少大部分时间是这样。例如, 如果编译器无法在编译时确定递归的深度,则几乎可以肯定不会内联生成递归内联函数。大多数编译器如果无法通过局部静态分析确定对象的实际类型,则无法内联生成虚函数,尽管其中一些最好的,给定的分析器输出显示,99% 的时间将调用一个特定的重载, 可能会生成一个if, 并内联该情况。

通常,您希望在头文件中定义尽可能少,因此对于“导出”类,inline除非分析器说绝对必要,否则您不会使用(显式或隐式)。对于在源文件中定义的本地类,是否在类定义中定义函数更多的是风格问题(编译器是否内联它们可能会或可能不会有所不同——正如我所说,使用通常的调试选项,大多数编译器不会内联任何东西)。

于 2012-11-14T10:01:32.577 回答