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.

// a.cpp
inline bool foo() { return false; }

// b.cpp
extern bool foo();

bool bar() { return foo(); }

Yup, that’s right. The C++ language says in section 3.2(3) [C++03, C++11], and repeats in section 7.1.2(4) [C++03, C++11],

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

The solution is to move the definition of the inline function into the header file.

Now you can solve this problem:

I’m getting error LNK2019 for my Get­Value method. Can somebody explain why?

// Widget.h
class Widget
{
public:
 Widget(int initialValue) : value_(initialValue) { }
 void SetValue(int value);
 inline int GetValue();
private:
 int value_;
};

// Widget.cpp
#include <widget.h>

inline int Widget::GetValue()
{
 return value_;
}

// Other.cpp

void something()
{
 Widget widget(42);
 printf("%d", widget.GetValue());
}
Comments (23)
  1. JB says:

    In these days of link time code generation I imagine that some of these "problems" could be solved.

    But can't be because 1) we don't want to enforce link time code generation, and 2) backwards compatibility

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

  3. Joshua says:

    Why doesn't the second yield a compiler error as its declared noninline and defined inline?

  4. Adam Rosenfield says:

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

  5. Chris Walken says:

    I would like to propose a new rule that says foo and bar can never ever ever be used in example code ever again. Ever.

  6. If @Chris Walken's proposed rule is accepted, then I propose that all example functions be named boots()

  7. John says:

    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.

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

  9. 12BitSlab says:

    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.

  10. James says:

    @Joshua: It's quite legal (from the compiler's POV) to use inline next to the definition but not in the declaration. (And it is the practice that some recommend: http://www.parashift.com/…/where-to-put-inline-keyword.html )

  11. Joshua Ganes says:

  12. Hold on, so other.cpp doesn't need to #include a definition of the class to make objects from it?  Just needs to link with it?

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

    This may seem like nit-picking, but actually I don't get put in charge of linking and #includes very much…

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

  14. Joshua says:

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

  15. Evan says:

    @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]
  16. Adam Rosenfield says:

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

  17. @Joshua

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

    I'm so glad I wasn't around for those days sometimes…

    Why was that?  Were the addresses of functions that did useful things in the start of addressable memory or was it more complicated?

  18. Joshua says:

    @Veltas: It's not immediately obvious whether it executed the interrupt vector table, or did near call indirect on a far pointer.

    Either way, one of the instructions reached before crashing outright happened to decode to "out [some port]" which happened to correspond to the ISA-IDE link and the byte in AL was the format command.

  19. Evan says:

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

  20. Paul Parks says:

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

  21. @Joshua Ganes: "Frankly, I've never found a use for them in any serious projects."

    Someday you should take a ride on the real-time embedded DSP train. Inline is useful when you really need that last extra clock cycle…

  22. Evan says:

    @Paul M Parks: "The "inline" keyword in standard C++ is…actually nothing more than a pitiful request for the compiler to please, if it's not too much trouble, inline the function"

    Don't forget the linker feature that I mentioned: it's also a directive to the linker to collapse multiple definitions of that function.

  23. Medinoc says:

    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.

Comments are closed.