Is there still a use for inline? [duplicate]

I believed, inline was obsolete because I read here:

No matter how you designate a function as inline, it is a request that the compiler is allowed to ignore: the compiler might inline-expand some, all, or none of the places where you call a function designated as inline.

However, Angew seems to understand something I don't. In this question he and I go back and forth quite a bit, on whether inline is still useful.

This question is not a question on:

  • The historical use of inline or where inline could still be used to hint to the compiler to inline functions: When should I write the keyword 'inline' for a function/method?.
  • The benefits or drawbacks of inlining function code: Benefits of inline functions in C++?
  • Forcing the compiler to inline function code: force inline function in other translation unit

Bearing in mind that the compiler can inline at will, so inline is not helpful there: Where can inline be used to force, not suggest, a change in compiled code?


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.


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...