首先为我的英语感到抱歉。
由于GCC完全忽略了内联说明符,所以我很难知道一个函数是否被我标记为内联。我想理解的是,当你有一个相同的函数的一些重新声明(在相同的翻译单元或在不同的),何时或在哪种情况下你的函数被标记为内联或不(不管编译器会用你的提示做什么)。
例如:inline void print();
void print();
或:
void print();
inline print();
用不同的内联说明符重新声明一个函数的意义是什么?一个更复杂的例子:
#include <iostream>
void print();
int main()
{
print(); // (1)
}
inline void print() { std::cout << "HELLO" << std::endl; }
从c++独有的角度来看,而不是从编译器的角度来看,print
函数在(1)
行是内联函数吗?
我不能简明地表述我的问题,但我认为消息已经收到:)我试图理解一个函数应该被标记为内联的时候,而不是从c++和程序员的角度来看(不管编译器会用你的函数做什么)。
这句话
由于GCC完全忽略内联说明符,这有点困难对于我来说,知道一个函数是否被我标记为内联
是一个不正确的起始基。(当然,在本讨论中,我们可以用任何其他现代编译器替换GCC)。
当然,编译器可能会忽略inline
关键字来决定函数是否内联,并且它当然可以内联未标记为内联的函数。但是,在生成最终输出时,编译器确实使用inline
关键字[它相当于"在结构体内部声明的体"],以避免在不同的编译单元中多次生成代码的函数的多个定义。例如:
foo.h:
inline int foo() { return 42; }
a.cpp:
#include "foo.h"
...
b.cpp:
#include "foo.h"
...
如果函数foo
没有声明为inline
,如果我们将a.p p和b.p p的结果链接到一个可执行文件中,链接器会抱怨函数foo
的多个定义。
然而,你是对的,编译器不会根据inline
关键字来决定内联函数,而是根据其他方面来决定,比如函数被调用了多少次,函数的源代码对编译器是否"可见",等等。
作为一个简单的规则:
- 编译器将不内联函数,它没有源代码。
- 编译器将不内联虚函数(除非它能确定虚函数所属对象的类型)
- 编译器将内联小函数和/或只调用一次的函数,特别是如果函数是静态的。
由于内联发生在编译结束时(当所有源代码都被解析并放置在某种AST(抽象语法树)或类似形式中时),函数是在使用点之前还是之后通常并不重要-当然,如果调用的函数不是AST的一部分(源代码不可用),编译器别无选择,只能不内联它。
"当源不可用时不能内联"的例外是所谓的LTO"链接时间优化"。传统的链接器只会收集一组机器代码指令,并按照它们列出的顺序将它们粘贴在一起,而不需要了解函数是什么[简化的视图,足以用于此讨论],然后为编译器无法直接解析的函数和变量修复任何地址。相比之下,LTO将"目标文件"存储为中间表示,最后的机器码生成由链接器阶段完成。这意味着链接器有足够的信息来移动代码,例如将一个函数中的代码内联到另一个函数中。该技术可以在最近的版本中使用,例如gcc(4.9.0将LTO作为一个完整的特性,早期版本的支持略少)
inline
允许在两个或多个翻译单元中定义外部链接函数(本质上相同)。
这意味着在头文件中定义它。
当一个函数是inline
时,它必须被定义,而inline
,本质上是1)在每个使用它的翻译单元中都是相同的。
inline
只是累积。函数不能变成un- inline
.
除了这些考虑之外,
inline
还可以作为一个优化提示,编译器可以自由地忽略它。在这种情况下,它声明您希望内联对该函数的机器代码调用。一般来说,编译器可以更好地判断这在哪里是好主意或不好。
1 c++ 17 10.1.6/5 (dcl.inline/5): "内联函数或变量应在每个使用它的翻译单元中定义,并且在任何情况下都应具有完全相同的定义"