内联还有用吗?

我相信,inline已经过时了,因为我读了 给你:

无论你如何指定一个函数为 inline,这都是一个编译器可以忽略的请求: 编译器可以在你调用一个指定为 inline的函数的地方内联展开一些、全部或者没有。

然而,安吉似乎理解了一些我不理解的东西。在 这个问题中,他和我就 inline是否仍然有用这个问题来来回回地讨论了很多次。

这个问题是 没有的一个问题:

请记住,编译器可以随意使用 inline,因此 inline在这里没有帮助: 在哪里可以使用 inline强制,< em > 不建议 ,编译代码的变化?

20961 次浏览

inline is mostly just an external linkage specifier now, for the reasons you stated.

So yes, it does have a use, but it's a different one than actually inlining functions. It allows you to define the same method multiple times between compilation units and properly link them together, instead of getting multiple definition errors.

//header.h
inline void foo() {}
void goo() {}


//cpp1.cpp
#include "header.h"


//cpp2.cpp
#include "header.h"


// foo is okay, goo breaks the one definition rule (ODR)

Actually forcing function inlining is up to the compiler, some might have support via specific attributes or pragmas or (__forceinline) or whatnot.

Simply put, it allows you to define functions in headers without breaking the ODR...

I will try to explain my "secret understanding" the best way I can.

There are two entirely separate concepts here. One is the compiler's ability to replace a function call by repeating the function body directly at the call site. The other is the possibility of defining a function in more than one translation unit (= more than one .cpp file).

The first one is called function inlining. The second is the purpose of the inline keyword. Historically, the inline keyword was also a strong suggestion to the compiler that it should inline the function marked inline. As compilers became better at optimising, this functionality has receded, and using inline as a suggestion to inline a function is indeed obsolete. The compiler will happily ignore it and inline something else entirely if it finds that's a better optimisation.

I hope we've dealt with the explicit inline–inlining relationship. There is none in current code.

So, what is the actual purpose of the inline keyword? It's simple: a function marked inline can be defined in more than one translation unit without violating the One Definition Rule (ODR). Imagine these two files:

file1.cpp

int f() { return 42; }


int main()
{ return f(); }

file2.cpp

int f() { return 42; }

This command:

> gcc file1.cpp file2.cpp

Will produce a linker error, complaining that the symbol f is defined twice.

However, if you mark a function with the inline keyword, it specifically tells the compiler & linker: "You guys make sure that multiple identical definitions of this function do not result in any errors!"

So the following will work:

file1.cpp

inline int f() { return 42; }


int main()
{ return f(); }

file2.cpp

inline int f() { return 42; }

Compiling and linking these two files together will not produce any linker errors.

Notice that of course the definition of f doesn't have to be in the files verbatim. It can come from an #included header file instead:

f.hpp

inline int f() { return 42; }

file1.cpp

#include "f.hpp"


int main()
{ return f(); }

file2.cpp

#include "f.hpp"

Basically, to be able to write a function definition into a header file, you have to mark it as inline, otherwise it will lead to multiple definition errors.


The last piece of the puzzle is: why is the keyword actually spelled inline when it has nothing to do with inlining? The reason is simple: to inline a function (that is, to replace a call to it by repeating its body on the call site), the compiler must have the function's body in the first place.

C++ follows a separate compilation model, where the compiler doesn't have access to object files other than the one it's currently producing. Therefore, to be able to inline a function, its definition must be part of the current translation unit. If you want to be able to inline it in more than one translation unit, its definition has to be in all of them. Normally, this would lead to a multiple definition error. So if you put your function in a header and #include its definition everywhere to enable its inlining everywhere, you have to mark it as inline to prevent multiple definition errors.

Notice that even today, while a compiler will inline any function is sees fit, it must still have access to that function's definition. So while the inline keyword is not required as the hint "please inline this," you may still find you need to use it to enable the compiler to do the inlining if it chooses to do so. Without it, you might not be able to get the definition into the translation unit, and without the definition, the compiler simply cannot inline the function.

The compiler cannot. The linker can. Modern optimisation techniques include Link-Time Code Generation (a.k.a. Whole Program Optimisation), where the optimiser is run over all object files as part of the linking process, before the actual linking. In this step, all function definitions are of course available and inlining is perfectly possible without a single inline keyword being used anywhere in the program. But this optimisation is generally costly in build time, especially for large projects. With this in mind, relying solely on LTCG for inlining may not be the best option.


For completeness: I've cheated slightly in the first part. The ODR property is actually not a property of the inline keyword, but of inline functions (which is a term of the language). The rules for inline functions are:

  • Can be defined in multiple translation units without causing linker errors
  • Must be defined in every translation unit in which it is used
  • All its definitions must be token-for-token and entity-for-entity identical

The inline keyword turns a function into an inline function. Another way to mark a function as inline is to define (not just declare) it directly in a class definition. Such a function is inline automatically, even without the inline keyword.