0

I have a following problem. I have these 3 files (I made a simplified example, but the errors are the same):

foo.hpp

#pragma once
#include <iostream>

class foo
{
protected:
    virtual void bar() const noexcept = 0;

public:
    foo() = default;
    virtual void callbar() = 0;
};

class baz : public foo
{
protected:
    void bar() const noexcept override;

public:
    void callbar();
};

foo.cpp

#include "foo.hpp"

inline void baz::bar() const noexcept { std::cout << "baz::bar()" << '\n'; }

inline void baz::callbar() { bar(); }

main.cpp

#include "foo.hpp"

auto main() -> int
{
    baz b;
    b.callbar();
}

Compiler (actually the linker I guess) gives me the following error:

foo.cpp
main.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 14.15.26729.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:foo.exe
foo.obj
main.obj
main.obj : error LNK2001: unresolved external symbol "protected: virtual void __cdecl baz::bar(void)const " (?bar@baz@@MEBAXXZ)
main.obj : error LNK2019: unresolved external symbol "public: virtual void __cdecl baz::callbar(void)" (?callbar@baz@@UEAAXXZ) referenced in function main
foo.exe : fatal error LNK1120: 2 unresolved externals

Now I've gotten around this by doing either one of those two things:

  • Remove inline keyword
  • Leave inline as it is, but move the methods definitions to .hpp file

If I do one of those things, everything works. But my question is: why? In my real code I really wan the compiler to inline the method calls plus I want them to be defined in .cpp file, to make my .hpp file more clear. Is there a solution to that?

dabljues
  • 1,663
  • 3
  • 14
  • 30
  • Why `inline` in definitions? – Algirdas Preidžius Apr 10 '19 at 14:51
  • Well, I know if the method is defined entirely in class body then it's inlined by default, but if I define it outside, then it's not, and I want it to be. Cuz those methods will be called extremely often and I don't want to lose performance due to function call overhead – dabljues Apr 10 '19 at 14:52
  • What do you think you getting by using `inline`? Removing the `inline` is the correct soution. – john Apr 10 '19 at 14:53
  • 1
    @dabljues You're misunderstanding what `inline` does. Surprisingly it has nothing to do with inlining functions. – john Apr 10 '19 at 14:53
  • Gosh, that's embarrassing. Now I know, thank you! Should I delete/close the question then? – dabljues Apr 10 '19 at 14:58
  • Possible duplicate of [C++ inline member function in .cpp file](https://stackoverflow.com/questions/3992980/c-inline-member-function-in-cpp-file) – Nellie Danielyan Apr 10 '19 at 15:00
  • [inline](https://en.cppreference.com/w/cpp/language/inline) – Aconcagua Apr 10 '19 at 15:23

1 Answers1

3

Your code has a bug in it. According to cppreference

The definition of an inline function or variable (since C++17) must be present in the translation unit where it is accessed (not necessarily before the point of access).

Obviously, when you put your definitions in .cpp file and call those functions from other translation units, this condition is not satisfied.

So your two alternative approaches both work because they either

  • Remove this condition altogether (when you remove inline specifier)
  • Make the definition visible

And last, but not the least - C++ inline specifier has nothing to do with function inlining. There are other, compiler-dependent ways to request the actual inlining, i.e. __forceinline in various compilers.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Thank you! This guy's answer, which @Aconcagua pasted: https://stackoverflow.com/a/5971755/1312382, suggests "Of course, this is in addition to the hint to the compiler that the function should be compiled inline into where it is used (avoiding a function call overhead).". So is he wrong then? – dabljues Apr 10 '19 at 15:03
  • 1
    @dabljues yep, this is obsolete for a while. Compilers nowadays completely ignore `inline` when it comes to function inlining, and do it based on their own heuristics algorithms. Moreover, inlining is increasingly a property of a call site, rather than a function itself - function could be inlined in one place, and called in another. – SergeyA Apr 10 '19 at 15:06
  • Thank you, that was really clarifying! – dabljues Apr 10 '19 at 15:07
  • @SergeyA compiler do actually still use `inline` declaration in their heuristics. And a function being inline has a lot to do with the function being inlined because it is not possible for the compiler to inline function calls across TU's. (although, it is possible for linkers). – eerorika Apr 10 '19 at 16:26
  • @eerorika I still maintain that `inline` is a property of the language, rather than actual inlining. For example, a function which is not declared inline will still be inlined if it is's definition is visible, provided it is inlineable. The opposite is also true - an inline function which is not inlineable won't be inlined. Bottom line - the specifier is not related. – SergeyA Apr 10 '19 at 17:32
  • @SergeyA I agree with everything, except for your conclusion. A rectangle can be very wide, but have a small area (if it has small enough height). A rectangle can be very narrow, but have a large area (if it is tall enough). Thus you might conclude that the area of a rectangle is not related to the width of the rectangle. But that would be a false conclusion: They are directly related. Your conclusion has the same problem. – eerorika Apr 10 '19 at 19:39
  • @eerorika it feels like splitting hairs to me. From practical perspective, I think we are really in agreement here. – SergeyA Apr 10 '19 at 19:42