C++隐式内联函数

C++implicity inline function

本文关键字:函数 C++      更新时间:2023-10-16

我在文档中读过关于内联函数的说明。我的文档说:内联函数有两种类型: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的每个方法,我都必须在类之外声明,对吧?如果这是真的,我可以在类内实现一个方法,而不需要内联函数吗。

谢谢:)

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

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

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

为什么需要禁用内联?编译器通常更清楚。。。如果是出于调试目的,只需禁用优化,或者至少禁用函数内联。您甚至可以在一个文件中使用杂注来完成此操作。无论如何,通常应该避免调试发布版本,当然有时也不可能避免。

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

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

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

带有内联说明符的函数声明(8.3.5、9.3、11.4)声明了一个内联函数。内联说明符指示在调用点优先于通常的函数调用机制。在调用点执行此内联替换不需要实现;但是,即使此内联替换省略,7.1.2定义的内联函数的其他规则应仍然受到尊重。

其他所有函数都可能是"非内联"的,尽管一旦打开优化,可能会发生许多奇怪的事情,例如,看看gcc-优化选项:

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

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

  • -finline-functions作为O3的一部分

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

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

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

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

除了用always_inline属性。这是未优化时的默认设置
通过将单个函数标记为noinline属性。

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

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

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

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

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

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

  • 当为调试设计的选项为给定(或优化已关闭),并且不会实际内联任何东西

  • 最佳编译器在最大限度优化时会完全忽略它已打开;函数是否内联将唯一地取决于关于编译器对代码和评测数据的分析,以及给出的函数将被内联在一个位置(它位于紧密环路),而不是在另一个。(与其他海报不同即使调用站点和函数定义在两个不同的翻译单元中。)

在这两个极端之间,许多编译器都做而不是模块间分析,并将,至少在一定程度上优化是打开的,"接受提示",至少在大多数时候是这样。例如,如果编译器无法确定编译时的递归。大多数编译器都无法如果他们无法确定实际的具有本地化静态分析的对象的类型,尽管最好的、给定的探查器输出,它揭示了过载将在99%的时间内被调用,可能会生成if,并且内联那个案例。

通常,您希望在头文件中尽可能少地定义,因此,对于"导出"类,您不会使用inline(显式或隐式)除非探查器说这是绝对必要的。对于本地类,在源文件中定义,是否是否在类定义中定义函数(它可能也可能对于编译器是否内联它们没有任何区别—作为我也就是说,使用通常的调试选项,大多数编译器不会内联任何东西)。