0

The following is a simple example for separate compilation:

// mod.cpp
#include <cstdio>

class MyModule {
public:
    void print_msg();
};

void MyModule::print_msg() {
    printf("hello from module\n");
}
// main.cpp
class MyModule {
public:
    void print_msg();
};

int main() {
    MyModule a;
    a.print_msg();
}

We can compile and run it with

g++ main.cpp -c -o main.o
g++ mod.cpp -c -o mod.o
g++ main.o mod.o -o main
./main

The above works fine, but if I move the definition of MyModule::print_msg inside the class:

// mod.cpp
#include <cstdio>

class MyModule {
public:
    void print_msg() { printf("hello from module\n"); }
};

I get an 'undefined reference' error for compiling main:

g++ main.cpp -c -o main.o  # OK
g++ mod.cpp -c -o mod.o    # OK
g++ main.o mod.o -o main   # undefined reference error
/usr/bin/ld: main.o: in function `main':
main.cpp:(.text+0x23): undefined reference to `MyModule::print_msg()'
collect2: error: ld returned 1 exit status

I know that the former is the standard way and the class definition should go to a header file, but I wonder why the second method doesn't work.

ihdv
  • 1,927
  • 2
  • 13
  • 29
  • When a function definition is inside the class it is implicitly inline. Which means it is not available outside of the translation unit in which it occurs. So the reference in main.cpp does not find the definition in mod.cpp – john Jun 23 '22 at 06:17
  • I assume this is just sample code for a test and you have heard of header files? – john Jun 23 '22 at 06:17
  • @john Yes, I know the class definition part in the first example should go to a header file, which should be included in both `main.cpp` and `mod.cpp` – ihdv Jun 23 '22 at 06:19
  • Plus your code does break the ODR (one definition rule). When a class definition appears in multiple translation units it is required that it be identical in all translation units. That's probably the formal reason why your code fails. – john Jun 23 '22 at 06:20
  • @john Just to confirm my understanding: Inline functions are directly compiled into binary without name mangling so their definitions must be accessible to all translation units before compilation. In my example the linker is searching for the mangled function name in `mod.o` but there isn't any, so the linker says it's an undefined reference. Is this correct? – ihdv Jun 23 '22 at 06:51
  • Name mangling has nothing to do with it. If the function is inline then it is not visible to other code. – john Jun 23 '22 at 07:10

1 Answers1

1

Functions defined inside the class are implicitly inline. C++ requires:

The definition of an inline function [or variable (since C++17)] must be reachable in the translation unit where it is accessed.

Since you only defined it in mod.cpp, no definition is reachable in main.cpp, and compilation fails.

Typically, you'd put the definition of the class, and the definition of all functions defined within it, in a header file to be included by all users of the class. The functions defined outside the class then go in a .cpp file. That way a single consistent definition of all the inline functions is available to all users of the class, and you're not repeating the definition of the class in each .cpp file manually.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Just to confirm my understanding: Inline functions are directly compiled into binary without name mangling so their definitions must be accessible to all translation units before compilation. Is this correct? – ihdv Jun 23 '22 at 06:33
  • 1
    @ihdv: They usually don't *exist* as independent functions; their code is inserted directly at point of use. When that happens, there's no name to mangle. `inline` is just a suggestion (the compiler can inline non-`inline` functions, or have an actual function separate from the call site(s) for `inline` functions); when it isn't actually inlined though, is still needs a common definition shared between all points of use (which is why it typically goes in a header for inclusion in every place that needs it). – ShadowRanger Jun 23 '22 at 07:07
  • 1
    @ihdv `inline` the keyword no longer bears any relation to inlining code. All it now means is "exempt from the One Definition Rule". It can have multiple definitions, but they must be identical. – Caleth Jun 23 '22 at 09:13