2010-03-23 88 views
18

如果我们在类定义本身内部定义一个成员函数,它是否必须以内联方式处理,或者它只是它可以忽略的编译器的请求。C++中的内联函数

+0

另请参阅http://stackoverflow.com/questions/908830/isnt-cs-inline-totally-optional/910686#910686 – 2010-03-23 19:48:31

回答

18

是的,在类体内定义的函数隐含地为inline。 (与其他声明为inline的函数一样,这并不意味着编译器必须在函数被调用的位置执行内联扩展,它只是允许允许“单一定义规则”的放宽,并结合要求必须在使用该功能的所有翻译单元中包含定义。)

2

这是一个可以忽略的编译器的请求。

+4

不,这是一项要求。如果一个函数是在类体中定义的,那么它必须被视为被声明为“inline”。 – 2010-03-23 16:39:05

+5

像编译器可以忽略的任何'inline'请求。 – shoosh 2010-03-23 16:41:22

+1

由于它可以被忽略,并且大多数编译器会在适当的时候内联,即使你没有请求它,这也是一个相当无用的提示。 – marr75 2010-03-23 16:56:46

3

它必然被编译器视为内联请求 - 它可以忽略它。在头文件中定义了一些函数(例如空的虚拟析构函数)和一些必要的头文件定义(模板函数),但有一些成语可以在GotW #33以获得更多信息。

一些人已经注意到,编译器甚至可能内联函数,你从来没有问过它,但我不确定这是否会打败请求内联函数的目的。

+0

取决于编译器。一般来说,内联是一个不太重要的暗示。我认为大多数针对嵌入式平台的编译器会更仔细地听取提示,这是经过仔细设计后,您可能会超越编译器的唯一空间。 – marr75 2010-03-23 16:54:35

3

确实是内联的 - 但编译器可以忽略任何内联请求。

2

2003年ISO C++标准说

7.1.2/2的函数声明(8.3.5,9.3,11.4)有一个内联 指定符的内联 功能。内联说明符 指示实现 函数 在调用点处的主体的内联替换为 首选通常函数调用
机制。在调用点执行此内联
替换所需的实现不是 ;然而,即使这个内联
替换被省略,内联函数的其他 规则仍应遵守由7.1.2定义的 。

7.1.2/3在类定义中定义的函数是内联函数
函数。内联说明符 不会出现在块范围函数 声明中。

7.1.2/4的内联函数应每翻译单元被限定在
其被使用而在每个
情况下(3.2)具有 完全相同的定义。 [注意:在 翻译单元出现其定义之前,可能会遇到对 内联函数的调用
。 ]如果将具有外部链接的功能 在一个翻译单元中内联地声明为 ,则其 应在其中出现 的所有翻译单元的所有 中内联声明;不需要诊断。一个 内联函数与外部 连接应在 所有翻译单元中具有相同的地址。静态局部变量 extern inline
函数始终指向同一对象 。
extern内联函数中的字符串字面值与
单位中的对象相同,为 。

+0

这是最有用的答案,但也是误导性的,一般来说,主流编译器专注于标准的“执行不需要执行这种内联替换”元素。 – marr75 2010-03-23 16:58:48

1

有一些不应该混为一谈两件事情:

  1. 你如何标记功能为是线上:在签名前使用内联定义它或者在申报点定义它;
  2. 编译器将如何处理这种内联标记:无论您如何将函数标记为内联,它都将被视为编译器的请求。
4

正如其他人所说的,一个类中定义的方法是自动请求内联的。 理解原因很有用。

假设它不是。您必须为这样的函数生成代码,并且在调用的任何地方,跳转到子例程指令都必须通过链接器引用该位置。

class A { 
public: 
    void f() { ... your code ... } 
}; 

每到这个代码是看到的,如果不是内联,编译器只能假设必须生成的,所以它会产生一个符号。假设它是这样的:

A__f_v:

如果该符号是全球性的,那么,如果你碰巧在不同模块中多次包括这个类的代码,你就必须在链接时乘法定义符号错误。所以它不可能是全球性的。相反,它是文件本地。

想象一下,你在多个模块中包含上述头文件。在每一个中,它都会生成该代码的本地副本。哪一个比根本不编译要好,但是当你真的只需要一个时,你会得到代码的多个副本。

这导致了下面的结论:如果你的编译器不打算内联一个函数,你最好在某处声明它,而不是要求它被内联。

不幸的是,什么是内联不是便携式的。它由编译器编写者定义。一个很好的经验法则就是在移除开销时,总是让每一行代码,特别是所有函数本身只是调用函数,内联。低于三行线性代码的东西几乎可以肯定。但是如果你在代码中有一个循环,问题是编译器是否允许它内联,更重要的是,即使按照你想要的方式,你会看到多少好处。

考虑这个内嵌代码:

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

它不仅几乎小到原型将在源代码中,而是由内嵌代码产生的汇编语言小于调用一个子程序会。所以这个代码更小,更快。

而且,如果你碰巧经过常量:

int c= add(5,4); 

它在编译时解析并没有代码。

在gcc中,我最近注意到,即使我没有内联代码,如果它是一个文件的本地代码,他们也会偷偷地内联它。只有当我在一个单独的源模块中声明函数时,它们才不会优化掉呼叫。

在光谱的另一端,假设您在1000行代码上内联请求。即使您的编译器足够愚蠢以致于无法使用它,您保存的唯一东西就是调用本身,其代价是每次调用它时,编译器都必须粘贴所有代码。如果您将该代码调用n次,您的代码会按例程* n的大小增长。因此,大于10行的任何内容几乎都不值得内联,除了仅称为非常少的特殊情况。其中一个例子可能是由另外两个人调用的私有方法。

如果您要求内联一个包含循环的方法,它只有在它经常执行少量次数时才有意义。但是考虑一个迭代一百万次的循环。即使代码被内联,在通话中花费的时间百分比也很小。因此,如果你的方法中有循环,无论如何这些方法往往会更大,但是值得从头文件中删除,因为它们a)往往会被编译器内联拒绝,b)即使它们被内联,通常不会提供任何好处

+0

你只是想到了我。如果我以普通的方式定义一个函数(这里有一个定义和一个实现),并且我把关键字放在内部(实现?定义?)前面,那么编译器会考虑内联它?这更好?我对内联函数的主要关注点是它们很丑并且使行很长。你是说我可以让我的性感看起来很容易阅读函数内联......不仅如此,而且我应该以这种方式标记小函数?我希望你是一个活跃的用户,并回复此评论。 – Ziggy 2011-08-12 18:19:28

+1

如果您使用原型定义了一个函数,并且在同一个标​​头文件中实现了内联标记,它将被内联。如果您的实现在另一个文件中,则编译器无法将其内联。这不是一个心智读者,它不知道要生成什么代码。我个人不介意在头文件中添加一些额外的代码,而不是写两次,但如果您愿意的话,您肯定可以做到这一点。 – Dov 2011-08-14 15:45:24

+1

@ziggy如果我正确理解你的问题,是的,如果你的函数很短,那么在头文件中直接定义它可以是一个巨大的运行时速度的胜利,同时稍微减慢编译时间,并且还需要任何符号内联函数被定义。这意味着如果你的内联代码引用了其他对象,你的头文件可能需要包含其他头文件。所以在重新编译的时候会有很大的代价。但是对于一般的方法(getter/setter,用私有变量来计算某些东西),内联是一个巨大的胜利。 – Dov 2011-08-14 16:05:13