Why am I getting LNK2019 unresolved external for my inline function?

More than once, I've seen somebody confused by how inline functions work.

I have implemented a few inline functions in one of my cpp files,
and I want to use it from other cpp files,
so I declare them as extern.
But sometimes I will get linker error 2019 (unresolved external)
for the inline functions.

An inline function shall be defined
in every translation unit in which it is used.

(A translation unit is the technical term for what
we intuitively can think of as a single cpp file and all the
files that it #includes.)

By putting the definition of foo in a cpp file,
you make its definition visible only to that cpp file and
no other cpp file.
When you compile b.cpp,
sees that you declared it as a normal external function,
so it generates a call to it like a normal external function.
On the other hand, when you compile a.cpp,
the compiler sees that foo is an inline function,
so it says,
"I don't need to generate any code yet.
Inline functions generate code at the point they are invoked,
not at the point they are defined."

Result:
b.cpp asks for a definition of foo,
but nobody provides it,
because the two declarations were inconsistent.
This is a violation of
7.1.2(4) [C++03, C++11]
which says
"If a function with external linkage is
declared inline in one translation unit,
it shall be declared inline in all translation units in which it appears;
no diagnostic is required."
The magic phrase no diagnostic is required means that the compiler
is not even required to report the error.
(You're lucky that it did!)

This rule makes sense when you think about the classical model of
compiling:
The compiler logically
takes the source code and sends it through the
preprocessor.
The result (the translation unit)
then goes into the compiler proper,
which learns about structures and classes and functions,
and it generates code based on what it sees in that
translation unit.
The compiler does not have access to other translation units,
so when compiling a.cpp it can't peek into
b.cpp and say,
"Hm, it looks like somebody is going to be calling foo
as a non-inline function,
so let me also generate a non-inline version of it."
And similarly,
when the compiler is generating code for the
bar function,
it doesn't peek into a.cpp and say,
"Hm, it looks like foo is actually an inline
function.
Let me go steal its definition from that other file."

In the last exercise, you put a declaration that the function is inline in the .h file, but the compiler still doesn't know where to find the definition of the inline code. I believe you have to put the the whole function definition in the header file as well.

@Brian EE: Correct. The exercise and the original example are semantically identical after preprocessing — Widget.cpp sees both the declaration and definition of GetValue(), but Other.cpp only sees the inline declaration (not the definition), which again violates the same clause cited by Raymond ("An inline function shall be defined in every translation unit in which it is used.").

@Joshua: Assuming you're referring to Widget::GetValue(), it is declared inline.

The magic phrase no diagnostic is required means that the compiler is not even required to report the error. (You're lucky that it did!)

What exactly does this mean? In this case the linker is reporting the error, but I assume by "compiler" you mean "toolchain". What would happen if the error were not reported? I guess the linker would just silently fail? How can it be legal to not report a fatal error? From a quick Google^H^H^H^H^H^HBing search, it seems the relevant bit of the standard is "If a program contains a violation of a rule for which no diagnostic is required, this International Standard places no requirement on implementations with respect to that program." So if you write a non-compliant program, and the rules you break are "no diagnostic required", then the toolchain can do whatever the hell it wants? Awesome! I'm going to write a compiler that formats your hard drive when one of these non-diagnostic rules is broken.

In C++, a function declared inline, gets static linkage by default (unless it's a member function). If I remember correctly.

John:

That means, if a function is not declared 'inline' in some compilation unit, and declared 'inline' in others, the definition might be different and violate One Definition Rule, but the compiler/linker won't be able to detect that and issue any diagnostic.

I must be REALLY old. In them there olden days, we would have written routines in ASM, compiled them them to an object, and then linked them in. Of course, back in those days, no compilers I knew of (outside of PL/I) could have inline code.

@Evan: Thanks! That clears it up for me, not used to worrying about headers etc. too much, just stick to a certain pattern and live by certain simple rules like "inline functions must be defined in a header file" funnily enough.

[I can imagine a toolchain where the attempt to find the named function is made, the attempt fails (because it was defined inline even though it was declared as non-inline), and a null pointer comes back, and then the code calls the null pointer and then you are clearly in undefined territory. I can imagine this because such a toolchain already exists. It's called Win16. -Raymond]

I was expecting that. And there is case on record where call (NULL + 7 * 2) resulted in formatting the hard disk, so you're not completely off on can format disk.

@John: "What exactly does this mean? In this case the linker is reporting the error, but I assume by "compiler" you mean "toolchain". What would happen if the error were not reported? I guess the linker would just silently fail?"

Not necessarily. For instance, you could write a toolchain that, at a function call, looks up in some global dictionary the name of the function and then jumps to that address. At program start, it would just populate that dictionary.

If you called an undefined function then, it could throw a runtime error, crash, or perform other undefined behavior.

Is this LIKELY? No. But it sounds like it's allowed by the standard, and in some sense it's not even *really* trying to be actively malicious and sounds like it could be the basis for an implementation if you really wanted it to be. (Actually it's about what Python does, for example.)

[I can imagine a toolchain where the attempt to find the named function is made, the attempt fails (because it was defined inline even though it was declared as non-inline), and a null pointer comes back, and then the code calls the null pointer and then you are clearly in undefined territory. I can imagine this because such a toolchain already exists. It's called Win16. -Raymond]

@John: Yes, you *could* do that, but then nobody would use your compiler. The reason the standard does not require a diagnostic in this instance is so that it can allow a conforming implementation which, upon seeing an inline function, chooses not to inline it and instead creates an external definition; it then merges all of the external definitions of that inline function from different translation units (the One Definition Rule) to avoid multiply defined symbols for legal code.

This will result in the original example compiling correctly (despite the error), and the compiler's behavior is completely reasonable. It does not seem sensible to require the compiler to diagnose this error, because then it would be much harder to write a simple, non-optimizing compiler.

@Raymond: "…a null pointer comes back, and then the code calls the null pointer and then you are clearly in undefined territory"

Nifty. Any idea why they didn't check for that case? Was it just a convenience issue, or was there some deeper reason why you might want to have that null call there?

[The language permits the implementation to do anything. Executing random garbage is a legal value for "anything". Why write code you don't have to? -Raymond]

@Joshua: "What's the biggest efficiency gain anyone reading this blog has found by changing to use an inline function?"

Depends what you mean.

Here's the deal. *Marking* a function as inline I suspect rarely does much. However, this is because the compiler will consider inlining functions unilaterally when optimization is on. I did some tests with two non-MS compilers a while back, and on the examples I tried about half the benefit of -O1 relative to -O0 is lost if you disable inlining (-fno-inline). (-O2 provided a similar amount of gain over -O1 as -O1 did over -O1 -fno-inline, or as -O1 -fno-inline did over -O0. Actually a bit less.) So inlining as an optimization buys a great deal of benefit.

(Said program actually makes somewhat heavy use of virtual functions as well, so there are plenty of calls to small functions which it is unable to optimize for that reason.)

However, without link-time code generation, the compiler is only able to inline a function — by your instruction or on its own — if the definition is available. If you have a function boots() which is defined in one compilation unit and called inside of a tight loop in another, the compiler won't be able to do anything. So that is legitimately missed opportunities.

So what does this mean? My takeaway is as follows. It really can buy quite a bit to make sure that your functions are inline-*able* by being available in the compilation units in which they are used. This doesn't necessarily mean that they are marked inline. However, if you want to use those functions across different compilation units and put them into a header as a result, you need some way to eliminate multiple definition linker errors — and marking them "inline" is probably the best way to achieve that end. So inlining the optimization is helpful, and "inline" the keyword is necessary for correctness.

The other side of the story is I'm not sure how much it would help to explicitly try to make function definitions inlineable, or whether you get most of the benefit just reasonably naturally.

(I'm not totally sure how __force_inline or however it is spelled plays into this.)

@Veltas: "Or did you #include "Widget.h" somewhere in other.cpp?"

Yes, you're correct. The definition needs to be visible. (Importantly in other contexts, you don't need a full class definition to make a pointer or reference: "class C; C* c;" is A-OK. But even there you need a declaration.)

@Joshua Ganes: The "inline" keyword in standard C++ is a bit weird. It's actually nothing more than a pitiful request for the compiler to please, if it's not too much trouble, inline the function. The compiler (or, more likely, the optimizer) can, and often will, do whatever it wants with the function's code. Indeed, a function not declare as inline may end up being inlined while one that is declared as inline may not be. The only way to guarantee inlining is through compiler-specific keywords like forceinline.

That said, the inline keyword is still useful because some functions should be defined in a header, such as particular overloaded operators. These should be declared as inline, even though the compiler and/or optimizer may not even inline them.

Visual's C++ rules seem to be different, with MSDN saying only that inline functions have static linkage by default. But *can* have external linkage.

In fact, in your code sample, replacing "inline int Widget::GetValue()" with a free function* "extern inline int GetWidgetValue(Widget &obj)" will do the trick. It actually tells the compiler to think "Hm, it looks like somebody is going to be calling foo as a non-inline function, so let me also generate a non-inline version of it."

*The function need to be free however, attempting it on a member function yields "warning C4630: 'Widget::GetValue' : 'extern' storage-class specifier illegal on member definition" in Visual C++ 2010.