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