Inline functions

In a recent thread, @Niccolo made the following statement.

Most people assume that is about optimization, but it isn't. It may give a hint to the optimizer, but the primary purpose for the inline keyword is when code (a function in particular) is in a header file. If that file, containing a function definition, is included in multiple translation units (multiple cpp's), the linker sees it duplicated in each one, and complains. The inline keyword informs the linker to expect the duplicates, ignore them and resolve to only one copy of that function in the output.


Is this (the last part) correct? I readily admit I'm not fluent in generated machine code, but my understanding was that inline functions were copied into each of the compilation units and either embedded (truly inlined) into the code if small enough or silently linked internally when too big for true inlining. Thus, each compilation unit would have a copy of any inlined functions that it includes. This allows object files and libraries to be linked without knowing which other object files contain the same inline functions (or differing inline functions with identical signatures).

I never really needed to think about it beyond this understanding, but I see now that it is possible for object files to keep copies of their inlined functions, and at link time throw out duplicates.

Would @Niccolo like to clarify this point at all? (And yes, I realize I am inviting another wall of text.) Would others like to chime in regarding how functions that are declared inline but remain as standalone functions make it through to the final output?

it looks to me like your understanding is correct.
the quoted text is just exploiting what you know already, using the inline keyword to overcome the duplicate function problem. you said it yourself:

This allows object files and libraries to be linked without knowing which other object files contain the same inline functions


functions that are declared inline but remain as standalone functions make it through to the final output -- personally, I fix these myself by manually inline them using the #include trick. If I want something inline, it will BE inline when I am done with it. Its been a while since I have been able to get a performance lift from doing it myself, though because compilers are really smart now. I don't like the idea of using inline as a form of include guards, but I guess it works. Hope they commented it.
Last edited on
An inline function with external linkage:
a. must be defined in every translation unit in which it is used (odr-used)
b. must have the same definition in every translation unit.
c. has the same address in all translation units.

a. and b. are the responsibility of the programmer; c. is the responsibility of the implementation.
Is this (the last part) correct?

Yes. This is why we now have inline variables in C++17 even though inlining as an optimization doesn't make sense for variables. It allows us to define global variables in header files.

This allows object files and libraries to be linked without knowing which other object files contain the same inline functions (or differing inline functions with identical signatures).

All definitions of the same inline function (or variable) must be identical otherwise you have undefined behaviour, no diagnostic required. You have the same problem with types.
Last edited on
Did anyone notice that @Peter87 is about to flip the odometer (post 9999 as I write this).

(And yes, I realize I am inviting another wall of text.)


Hey, I resemble that remark.
@doug4,

At this point I see you've heard from a number of viewpoints confirming the overall notion that the "inline" is commonly misunderstood to mean optimization, and that most ignore that it's main purpose to inform the linker that duplicate code is coming, typically from function definitions in header files.

Peter87 points out the new use for variables, which offer a convenience over the notion of declaring data as extern, then carefully choosing some translation unit to define it once and only once.

That's the opposing point with functions, too. Essentially stand alone functions (in particular) are all "considered" to be extern, which is to say the declaration found in a header file may be found by the linker in some other translation unit at link time. In that situation if, by some mistake, a definition appeared in two or more translation units it is a linker error (or at least a warning), since the linker can't decide for you (even though some switching might force it to) which of the duplicates to use.

The inline keyword, when applied to functions, addresses that opposite scenario typically implemented as a definition in a header file where such duplicates are to be expected and because they come from the same text in each repetition, it should be safe to assume just one of them is applicable (and if it is short, emitted as inline assembler, leaving no actual function call).

What many don't stop to think is a situation that comes, often by accident, where a function declared in a header, provided in one translation unit, is short (and appears early in the CPP file). Later in that CPP file, the compiler processes a function that calls this short function. It is not declared inline. It isn't defined in the header. Yet, the optimizer is just as likely to emit this short function as inline machine code because it fits all the requirements (and it now knows that code since the short function was already compiled).

This leads to a great frustration and name calling of various compilers, where programmers think the compiler is refusing to optimize and emit inline machine code for their functions when declared inline. They assume the primary purpose of inline is about emitting inline machine code. I'm not a language lawyer (and happy not to be), but I'm of the assumption (clearly an unsafe practice) that the standards don't mention much about a guarantee of any optimized inline machine code. Even with the ever popular __force_inline (or whatever a particular compiler might call it), there is no real guarantee of the emitted machine code. Some compilers seem more cooperative than others.

Ok, I'm going to pout in another forum over being exposed as a TL;DR poster
right. you can absolutely force inline yourself, but NOT with inline type suggestion statements. and again, as often as not, doing this is not helping your speed. Once in a while you win one. Visual studio used to respect force inline pretty well, so long as the function was not recursive or nested in some way that made it unpossible to unravel the inline chain. It probably still behaves this way. But you can't expect that on other compilers. If you want it inline for sure, DIY.
Last edited on
The compiler cannot perform inline substitution of a function unless the definition of that function is visible. But if a function is inline, its definition is visible to every translation unit (TU) in the program that needs it.

Otherwise, link-time optimization is required in order to optimize code across TUs.
Last edited on
most of the details have been explained already. I would just like to add that you can be informed if you wish by the compiler if function is not inlined.

in Visual Studio, that is msvc compiler throws you a level 4 warning (C4710)

https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4710?view=vs-2019

I do a lot of inlining in my project, mostly 1-2 line functions, and can say that so far 98% of my functions have been inlined so compiler does respect the inline keyword, the trick is to inline only single line or max 3 line functions.
Last edited on
Topic archived. No new replies allowed.