3

I recently learnt about inline functions in C++. I understand how they work (in terms of expanding the code instead of making jumps etc), however I'm a little confused regarding how I should use them when it comes to multiple translation units / *.cpp files.

I've read that, as a rule of thumb, I should contain the definition of the function itself inside a header file (and not just its declaration) and then include the header file in each translation unit I aim to use said inline function. That is not a problem for an inline function, since it will just exist standalone on each translation and expand itself when/if needed (decided by the compiler) [ please correct me if I am wrong regarding any of this!]

However, I also read that during link time, even if the definition of an inline function itself is not present in the header file, the linker can find its definition and expand it if needed. But how would the linker be able to expand a function from a different translation unit? And if that's the case, why would I need to include the definition of the function in each translation unit (through header files)?

Pol
  • 185
  • 9
  • 2
    there are two meanings of "inline" and I am not sure if you are aware of the difference. Whether the compiler inlines a function call is nowadays completely up to the compiler (and has nothing to do with the `inline` keyword). You can give the compiler a hint, but afaik the compiler just ignores it, because it knows much better what to inline and what not. The `inline` keyword is to be able to define functions in headers – 463035818_is_not_an_ai Oct 16 '20 at 11:57
  • Please do some research about [*Interprocedural optimization*](https://en.wikipedia.org/wiki/Interprocedural_optimization). Especially LTO (Link-Time Optimization). – Some programmer dude Oct 16 '20 at 11:57
  • I indeed didn't know this @idclev463035818 However, if I define a function as inline in a header file, it would now expand on each function call (still) and not make a jump everytime? Also, I'm still a bit confused how the linker would inline a function whose definition is a different translation unit during link time? (assuming we only have the function's definition in one of the transaltion units) – Pol Oct 16 '20 at 12:03
  • 1
    if you declare a function as `inline` in a header then it might be inlined or it might be not inlined ;) I know it is confusing, its due to history. In the past `inline` was indeed to tell if the method should be inlined on each call or not. – 463035818_is_not_an_ai Oct 16 '20 at 12:05
  • related: https://stackoverflow.com/questions/3647053/what-is-are-the-purposes-of-inline – default Oct 16 '20 at 13:14

1 Answers1

6

The inline keyword in front of a function was only an optional indicator for the compiler that inline is preferred. Modern compilers don't care about the inline keyword when it comes to code inlining.

The important meaning about inline is "multiple definitions are permitted", member functions definitions implicitly inline if they are in the body of the class.

You need inline if you have the same function definition in multiple translation units (e.g. if you place the function definition in a header, and that header is included by multiple cpp files)

I've read that, as a rule of thumb, I should contain the definition of the function itself inside a header file (and not just its declaration) and then include the header file in each translation unit I aim to use said inline function. That is not a problem for an inline function, since it will just exist standalone on each translation and expand itself when/if needed (decided by the compiler)

Placing a function definition in the header makes it easier for the toolchain to do inlining because it knows its definition when it is used and can perform the inlining in the compilation step. Having a function definition in multiple translation units requires it to be marked with inline.

However, I also read that during link time, even if the definition of an inline function itself is not present in the header file, the linker can find its definition and expand it if needed. But how would the linker be able to expand a function from a different translation unit?

A toolchain could do link-time optimization (like inlining) for any function (no matter if their definition is known at compile time) for which the definition is known at linking time (static linking), but link-time optimizations tend to - at least in the past - not as efficient as compile-time optimizations.

The problem with link-time optimizations is, that toolchain either needs to keep track of the additional information provided by the source code that is helpful for optimizations or needs to rely on possible less powerful the strategies you can apply on the binaries.

If you have a main.cpp that looks like this:

int sum(int a, int b) {
  return a+b;
}

int main() {
  std::cout << sum(3,4) << std::endl;
  return 0;
}

Any modern compiler will inline that code (or in that case event completely optimize it aways to std::cout << 7 << std::endl;).

Also this will be fine and inlined/optimized:

sum.h

int sum(int a, int b) { // no inline keyword in front of the function
  return a+b;
}

main.cpp

#include "sum.h"
int main() {
  std::cout << sum(3,4) << std::endl;
  return 0;
}

But as soon as you use #include "sum.h" in multiple cpp files and link those translation units together you get a problem and need to use inline. inline in front of a function is nowadays really only there to tell the linker that having the same definition in multiple translations units is your intent.

t.niese
  • 39,256
  • 9
  • 74
  • 101
  • I already got an answer to this but just to make it 100% clear: when I declare the function as inline in the header, will that not dictate whether or not it will expand on each function call? So for instance, I can have a function in a header defined as inline and it's still possible (for when I call the function) to have a jump happen instead of an expansion inside of the translation unit from which I make the call. Therefore, the 'small function'= 'inline' for faster execution is deprecated? – Pol Oct 16 '20 at 12:10
  • 2
    @pol `when I declare the function as inline in the header, will that not dictate whether or not it will expand on each function call?` yes that's fully up to the compiler, it may or may not inline a function no matter if there is an `inline` before it. Whether it does it or not depends on what the compiler thinks is better. – t.niese Oct 16 '20 at 12:11
  • @pol `Therefore, the 'small function'= 'inline' for faster execution is deprecated?` depends what you mean. Moving a small function (or one you like to be inlined) in a header (if it is used in multiple translation units), so that it can be more likely or easier inlined by the compiler, imho still holds. And if you define it in the header you need to add `inline` keyword in front of it so that the linker won't complain about multiple definitions. If a function is only used in one of you cpp files, you can just define it in there, without the `inline` keyword. – t.niese Oct 16 '20 at 12:14
  • And when expansion doesn't happen, does a regular jump happen? So the function would need to exist in the .text segment regardless then, in the case where inlining doesn't happen – Pol Oct 16 '20 at 12:16
  • @pol if a function is used multiple times, it could be that it is inlined at certain parts in the code, and for others not, there is no requirement that it has to be inlined everywhere. So yes as soon as no inlining happens at one place there would be some code for the function to which a jump exists (but only for those parts where the inlining was not applied) – t.niese Oct 16 '20 at 12:19
  • As you say, 'inline in front of a function is nowadays really only there to tell the linker that having the same definition in multiple translations units is your intent', withthe added bonus that there is a chance it will just be a code expansion wherever the function is used instead of wasting time on execution of jumps and stack manipulation (correct me if I'm wrong regarding the last part)! One last thing: if I make a function inline, whichexists only in a single translation unit, would this give more incentive to the compiler to expand it in any case? Thank you! – Pol Oct 16 '20 at 13:19
  • @pol check the two examples in the answer, in both cases, it does not make any difference (regarding the possible expansion of the code) whether you place `inline` in front of it or not. – t.niese Oct 16 '20 at 14:03
  • Therefore, what's the downside of having most functions inside a header file and making them inline, if I can even 'squeeze' some extra performance in some cases without really making a dent in compilation time? (since the 'inline' keyword is mostly ignored in terms of expanding the code) – Pol Oct 16 '20 at 14:51
  • 1
    @pol having most of the definitions in the header file can hurt compilation time, and is also not always possible depending on the complexity of the program and the cross dependencies. It always depends on the exact use case if you whether you want to have the definition in the header or not. – t.niese Oct 16 '20 at 14:56