为什么在标题中的C ++内联函数?

注意这不是一个关于如何使用内联函数或者它们如何工作的问题,更不是为什么它们按照它们的方式来完成。

类成员函数的声明不需要将函数定义为inline ,它只是函数的实际实现。 例如,在头文件中:

 struct foo{ void bar(); // no need to define this as inline } 

那么为什么类函数的内联实现必须在头文件中呢? 为什么我不能把内联函数放在.cpp文件中? 如果我在哪里尝试将内联定义放在.cpp文件中,我会得到以下错误:

 error LNK2019: unresolved external symbol "public: void __thiscall foo::bar(void)" (?bar@foo@@QAEXXZ) referenced in function _main 1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe : fatal error LNK1120: 1 unresolved externals 

inline函数的定义不一定要在头文件中,但是由于内联函数的一个定义规则 ,函数的相同定义必须存在于每个使用它的翻译单元中。

最简单的方法是将定义放在头文件中。

如果你想把一个函数的定义放在一个源文件中,那么你不应该声明它是inline 。 没有内联的函数并不意味着编译器不能内联函数。

无论你是否应该声明一个inline函数,通常都是你应该根据哪一个定义规则的版本来做出select,这对你来说是最有意义的。 增加inline ,然后受后续约束的限制是没有意义的。

有两种方法来看看它:

  1. 内联函数是在头文件中声明的,因为为了内联函数调用,编译器必须能够看到函数体。 对于一个天真的编译器来说,函数体必须与调用位于同一个翻译单元中。 (现代编译器可以跨翻译单元进行优化,因此即使函数定义位于单独的翻译单元中,也可以内联函数调用,但是这些优化是昂贵的,不总是启用的,并且并不总是由编译)

  2. 函数中声明的函数必须inline标记,否则包含头的每个翻译单元将包含该函数的定义,并且链接器将抱怨多个定义(违反One Definition Rule)。 inline关键字取消了这一点,允许多个翻译单元包含(相同的)定义。

这两个解释实际上归结为inline关键字不完全符合您的期望。

只要不改变程序的可观察行为,C ++编译器可以自由地应用内联优化 (将被调用函数的主体replace为函数调用,节省通话开销)。

inline关键字使编译器更容易应用这种优化,允许函数定义在多个翻译单元中可见,但是使用关键字并不意味着编译器必须内联函数,而不是使用关键字doesn' t禁止编译器内联函数。

这是C ++编译器的限制。 如果将函数放在头文件中,所有可以内联的cpp文件都可以看到函数的“源”,并且内联可以由编译器完成。 另外,内联必须由链接器完成(每个cpp文件分别在obj文件中编译)。 问题是在链接器中做这件事会困难得多。 “模板”类/函数存在类似的问题。 他们需要由编译器实例化,因为链接器将有问题实例化(创build一个专门的版本)。 一些较新的编译器/链接器可以做一个“两遍”编译/链接,编译器首先执行,然后链接器完成它的工作并调用编译器来parsing未解决的事情(内联/模板…)

原因在于编译器必须实际看到定义才能将其放入调用的位置。

请记住,C和C ++使用一个非常简单的编译模型,编译器一次只能看到一个翻译单元。 (出口失败,这是唯一一家供应商实际执行的主要原因。)

c ++ inline关键字是误导性的,并不意味着“内联这个函数”。 如果一个函数被定义为内联,它只是意味着只要所有的定义相同,它就可以定义多次。 对于标记为inline函数来说,这是一个真正的函数,而不是在被调用的地方插入代码。

模板需要在头文件中定义一个函数,因为例如一个模板类不是一个真正的类,它是一个类的模板 ,你可以做多种变化。 为了使编译器能够在使用Foo模板创buildFoo类时创build Foo<int>::bar()函数, Foo<T>::bar()的实际定义必须是可见的。

因为编译器需要查看它们以便内联它们。 头文件是通常包含在其他翻译单元中的“组件”。

 #include "file.h" // Ok, now me (the compiler) can see the definition of that inline function. // So I'm able to replace calls for the actual implementation. 

我知道这是一个古老的线程,但认为我应该提到的extern关键字。 我最近遇到这个问题,解决如下

Helper.h

 namespace DX { extern inline void ThrowIfFailed(HRESULT hr); } 

Helper.cpp

 namespace DX { inline void ThrowIfFailed(HRESULT hr) { if (FAILED(hr)) { std::stringstream ss; ss << "#" << hr; throw std::exception(ss.str().c_str()); } } }