I have a class that had an inline member, but I later decided that I wanted to remove the implementation from the headers so I moved the members body of the functions out to a cpp file. At first I just left the inlined signature in the header file (sloppy me) and the program failed to link correctly. Then I fixed my header and it all works fine, of course.
But wasn't inline totally optional?
In code:
First:
//Class.h
class MyClass
{
void inline foo()
{}
};
Next changed to (won't link):
//Class.h
class MyClass
{
void inline foo();
};
//Class.cpp
void MyClass::foo()
{}
And then to (will work fine):
//Class.h
class MyClass
{
void foo();
};
//Class.cpp
void MyClass::foo()
{}
I thought inline was optional, and imagined I might get by with a warning for my sloppiness, but didn't expect a linking error. What's the correct/standard thing a compiler should do in this case, did I deserve my error according to the standard?
Best Answer
Indeed, there is this one definition rule saying that an inline function must be defined in every translation unit it is used. Gory details follow. First
3.2/3
:And of course
7.1.2/4
:However, if you define your function within the class definition, it is implicitly declared as
inline
function. That will allow you to include the class definition containing that inline function body multiple times in your program. Since the function hasexternal
linkage, any definition of it will refer to the same function (or more gory - to the sameentity
).Gory details about my claim. First
3.5/5
:Then
3.5/4
:This "name for linkage purposes" is this fun thing:
Since now you have multiple definitions of the same entity in your programs, another thing of the ODR happens to restrict you.
3.2/5
follows with boring stuff.I cut off some unimportant stuff now. The above are the two important one to remember about inline functions. If you define an extern inline function multiple times, but do define it differently, or if you define it and names used within it resolve to different entities, then you are doing undefined behavior.
The rule that the function has to be defined in every TU in which it is used is easy to remember. And that it is the same is also easy to remember. But what about that name resolution thingy? Here some example. Consider a static function
assert_it
:Now, since
static
will give it internal linkage, when you include it into multiple translation units, then each definition will define a different entity. This means that you are not allowed to useassert_it
from an extern inline function that's going to be defined multiple times in the program: Because what happens is that the inline function will refer to one entity calledassert_it
in one TU, but to another entity of the same name in another TU. You will find that this all is boring theory and compilers won't probably complain, but i found this example in particular shows the relation between the ODR and entities.What follows is getting back to your particular problem again.
Following are the same things:
But this one is different, since the function is non-inline. You will violate the ODR, since you have more than one definition of
f
if you include the header more than onceNow if you put
inline
on the declaration off
inside the class, but then omit defining it in the header, then you violate3.2/3
(and7.1.2/4
which says the same thing, just more elaborating), since the function isn't defined in that translation unit!Note that in C (C99), inline has different semantics than in C++. If you create an extern inline function, you should first read some good paper (preferably the Standard), since those are really tricky in C (basically, any used inline-definition of a function will need another, non-inline function definition in another TU. static inline functions in C are easy to handle. They behave like any other function, apart of having the usual "inline substitution" hint. static
inline
in both C and C++ serve only as a inline-substitution hint. Since static will already create a different entity any time it's used (because of internal linkage),inline
will just add the inline-substitution hint - not more.