The reason that you get a linker error on that program is that your program uses the function maxf
, which is in the math library, and you don't link with -lm
. This only happens at certain optimisation levels.
Note that it's possible for the compiler to decide to neither inline nor call maxf
because it can see that the variable max
is unused, and therefore actually saving a value to it is unnecessary. Provided that the compiler knows that maxf
has no visible side-effects (and it knows that because maxf
is a standard library function), it can therefore eliminate the assignment to max
and therefore the call to maxf
. In fact, the compiler might even know what the semantics of maxf
are, and avoid the call by precomputing at compile time.
In order to avoid this case in your benchmark, you should do both of the following:
- Change the name of
maxf
so that it doesn't collide with a standard library function name. (Below I assume you renamed it as myMax
.
- Either declare the variable
max
as volatile
or use it in some non-predictable way, so that the store happens.
You should also fix the prototype of your function, since it's being called with a double
argument.
Now, let's suppose that you've fixed your benchmark accordingly, and you compile it. You will still receive the linker error at certain optimisation levels, because without optimisation (or in the case of Clang, at optimisation level less than 2), inlining is not performed. (In GCC at level -O1, the function is only inlined because there is just one call to it in the translation unit. Had it been called in two places, you'd need optimisation level 2 to trigger inlining, as with Clang.)
But it's clear that there is a definition of maxf
(or, in the corrected code, myMax
or some such). So why can't the compiler use it?
The answer has to do with the actual semantics of inline
, which might help answer your first question. As we all know (and can see in the lengthy discussion above), the compiler makes a decision about inlining functions more or less independent of the programmer's suggestion in the inline
specifier. (Compare this with the now-deprecated register
specifier, which compilers have been ignoring for a long time.)
But inline
does have an important significance. While the compiler can figure out, based on its own heuristics, code analysis and optimisation settings, whether or not it's a good idea to inline a function, it cannot know what your intentions are about use of that function in other translation units which will be linked to create the executable.
Perhaps in some other translation unit, the function myMax
is called as an external function. In that case, the compiler will need to include a compilation of myMax
whether or not it chose to inline all uses in this file.
On the other hand, it's possible that myMax
is intended to be inlined in every translation unit in which it appears; in other words, all translation units include a definition of myMax
. But that would lead to a problem, because those various definitions would collide when the translation units were linked together, generating a different linker error. (Duplicate name definition.) You could get around that by declaring the function static
, but in that case you might find that various modules in your executable each had their own static definition of the function, because the compiler chose not to inline it in those modules. That could significantly increase executable size.
And that's where the inline
declaration comes in. An inline
declaration of a function means "this is the definition to use in cases where this function is being inlined". If the compiler sees a function declared inline
without being explicitly declared extern
, it does not emit a definition of the function even if it chooses to not inline it. It relies on the function being defined in some external translation unit.
So you can safely put an inline
declaration in a header file. That will ensure that all inline uses of the function use the same definition, without causing any problems with duplicate names in the linker. But you still need to ensure that the function has a definition in exactly one translation unit. (This is somewhat similar to the related issue of declaring global variables used by various translation units in a program. The global variable must be declared consistently in all translation units, but defined only in one.)
In the case of inline
functions, you indicate that a definition of the function should definitely be compiled by using an extern
delaration. If you've declared myMax
as inline
in your header file, you can satisfy this requirement by putting the following in exactly one implementation file:
#include "myMax.h"
extern double myMax(double a, double b);
Note that you don't need the definition -- the one in the header will be used -- but you do need to get the prototype right.