Inlining functions can improve or worsen your program’s performance (however you define ‘performance’). It has been described in detail in Herb Sutter’s GotW #33. Compiler can decide to inline your function, even if it was not declared inline
and conversely: it can decide not to inline it even if it is declared inline
. So, you might be tempted to think that declaring functions as inline has no useful portable meaning in C++. This is not so. I have found inline functions useful, and its usefulness has nothing to do with inlining.
In order to be able to inline a function, compiler has to have its definition available at any time (at least this is the case for compilers that do not perform heavy link-time optimizations), in any compiled source file (or translation unit, if you will). In order to provide that, for inline functions it is allowed and in fact required that their definition (their body) is defined in every translation unit in which the function is used.
Contrast this with normal (non-inline) functions: if you define your function twice in two source files, you will get a linker error: a function can be defined only once. But not inline functions: their definitions can be duplicated. Compiler is not allowed to complain about multiple definitions.
Duplicated definitions, however, must define exactly same function. Not only should they be identical token by token, but the same name of a variable must refer to the very same object in each source file. For instance, the following is likely an invalid definition of an inline function:
// content of header file "file.h" namespace { int i = 0; } inline int get_i() { return ++i; }
If you include the above header from more than one source file, although the function body will look identical in each file, each function will refer to a different variable i
. This will render an ill-formed program, but compilers are not required to diagnose this.
Back to the key sentence above, “compiler is not allowed to complain about multiple definitions” — this is the feature of inline functions that I find useful.
First usage of this property that I ever encountered was when I wanted to change a function template into an ordinary function. This is a well known restriction on function templates: if you want them to be instantiated for types not known in advance, you have to provide their bodies in header files and have them included everywhere. This makes such function templates similar to inline functions. In this case it doesn’t matter if such function template is small or huge, or whether inlining the call increases or decreases performance. It just has to be inline, because this is how templates work.
I realized that I needlessly created a function template where a normal function would suffice. I was only using one instantiation. So, I changed my header file from:
template <typename T> T operation(T const& a, T const& b) { return some_code_here(a, b); }
to
double operation(double a, double b) { return some_code_here(a, b); }
And now my program failed to link due to multiple definitions of function operation
. True, I could have changed it into declaration, add a CPP file and put the definition in there, but while doing refactoring in a big project and under risking of leaving it in a broken state, I wanted to make as little changes as possible and have it build as soon as possible. Adding another CPP file would require of me integrating it with version control system, adding it to makefile; I decided it was too big an effort for that moment. Instead, I changed the header to:
inline double operation(double a, double b) { return some_code_here(a, b); }
And the project built smoothly. Not an ideal ultimate solution, but a good “checkpoint” when you want to refactor in small steps.
This characteristic of inline functions is often useful in other contexts. Every function defined inside class definition is an inline function. It is so obvious that you probably never even think about it. Otherwise defining classes with getters and setters would be a pain. It is less obvious that this rule also applies to non-member functions defined inside class:
class Span { double begin_; double end_; friend bool operator==(Span const& a, Span const& b) { return a.begin_ == b.begin_ && a.end_ == b.end_; } };
Thus defined operator==
is also an inline function, even though it is not a member. I heard about this useful technique from Sean Parent in this talk. There is a good reason for making operator==
a non-member function in order for it to have a symmetric behaviour. It has been recently described by John Lakos in this presentation. In short, the goal is to make sure that if expression a == b
compiles and works, expression b == a
should also compile and work with the same semantics. But if you define operator==
as a member function, you may break this rule; consider:
class XSpan { // ... public: operator Span() const; // no operator== }; Span s; Xspan x; s == x; // OK: s.operator==(x.operator Span()); x == s; // ERROR: no Xspan::operator==
So, there is a motivation for making operator==
a non-member. This also applies to any binary operation that is supposed to work symmetrically on two values; just imagine function overlap()
on two Span
s. It is also desirable to put operator==
inside the class’s definition, because it is part of Span
’s interface.
But we still could declare it in a header file and implement the function in a source file. True, but if operator==
is small why not put it in a header? Also, there is a trend to offer header-only libraries. They offer a number of advantages. You do not have to get concerned with where the compiled sources are located, which version of the compiled library (debug or retail) to link with, what platform they were compiled on, etc..
In short: both template and inline symbols are weak symbols.
Nice subject. When i have to explain what inline is about, i have to create a new wording : on one side in-lining on the other side inlining. That’s just a language trick, but that is how i understood and explain them :
In-lining in entirely up to the compiler and you can’t influence it (that’s what simply said is an optimization that copies the function code instead of calling it).
Inlining is about multiple definition, and is either implicit (template, class definition) or explicit (keyword inline).
Though i wonder, does inlining makes it easier for the compiler/linker to in-line ?
Would `static` be even better than `inline` in these cases? With `inline` you are required to ensure that each definition is identical, as already stated in this blog. I *think* this requirement is relaxed with `static` and therefore if you (accidentally) have different definitions then you will still have a well-defined program.
You cannot I think have declarations of `static` functions – they must always have a definition immediately. This is the only disadvantage of `static`, compared to `inline`, that I can think of.
I feel like I’ve missed something important though! Feedback welcome!
I was talking about functions in header files, which are included in multiple translation units.
You can put functions with internal linkage (declared static or in an anonymous namespace) in headers, and have them do something different in each file, but that sounds really terrifying.
static works well as well as anonymous namespaces… no need for inline here.
That is, where?
do you mean this code?
Hi,
I meant what Aaron said above. Inline sounds more like an hack in your case and static is actually what you want to achieved.
However, I must admit than the fact that whith inline you are required to ensure that each definition is identical is a nice to have even though I think this does not compensate the fact that if at one point you need to change the function you may need to do it at several location.
If it’s static, the compiler/linker will probably not deduplicate it across translation units. If it’s an anonymous namespace, it almost certainly will not. That could lead to code bloat.
Also, some compilers have a warning for static functions that are not used in a TU. If the static function is in a header, the compiler would warn for every inclusion of the header if the function is not used – multiple warnings if there are several functions. And it’s quite a useful warning otherwise.
“If it’s static, the compiler/linker will probably not deduplicate it across translation units.”
Not “probably”; definitely. For the “static” version, each instance of the function (because there are several) must have its own address. For the “inline” version, the function (because there is only one) must only have one address. All of this is mandated by the spec.
The bottom line is that the “inline” keyword in C++ has nothing to do with inlining. It has to do with linkage, full stop, end of story. And “static” is not the same at all, nor is it preferable in any conceivable use case.
Nice post.
The compiler can easily determine that a function’s address is not taken in a given TU and therefore mark the static function as available for deduplication, if it knows how to prevent folding with a function of the same name that doesn’t have the same content.
I think the sentence “It just has to be inline, because this is how templates work.” is potentially a little misleading. Template functions have to be defined in every translation unit that instantiates them (which usually means putting them in a header file) but they are not implicitly inline.
You may still want to explicitly declare a template function inline (indicate that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism) as a separate decision from the requirement to make the template definition visible. With modern compilers increasingly making their own decisions about inlining calls and not necessarily paying much attention to the user’s request this may not be as necessary these days but you are reasonably likely to see code that does this so it’s worth understanding the distinction!
Why do you repeat what main article says?