Ant
Ant

Reputation: 5414

Confusion over inline behavior C++

I am learning C++ and I am confused by inline behavior. On cppreference I found that " function included in multiple source files must be inline". Their example is the following:

// header file
#ifndef EXAMPLE_H
#define EXAMPLE_H
// function included in multiple source files must be inline
inline int sum(int a, int b) 
{
    return a + b;
}
#endif

// source file #2
#include "example.h"
int a()
{
    return sum(1, 2);
}

// source file #1
#include "example.h"
int b()
{
    return sum(3, 4);
}

This is a bit confusing to me - I thought that the ifndef guard was doing this job exactly, i.e. preventing problems when the same file was included multiple times. In any case, I wanted to test this, so I prepared the following:

// Sum.h
#ifndef SUM_H
#define SUM_H
int sum(int a, int b);
#endif

// Sum.cpp
int sum(int a, int b){
    return a + b;
}

// a.h
#ifndef A_H
#define A_H
int af();
#endif

// a.cpp
#include "sum.h"

int af(){
    return sum(3, 4);
}

// b.h
#ifndef B_H
#define B_H
int bf();
#endif 

// b.cpp
#include "sum.h"

int bf(){
    return sum(1, 2);
}

// main.cpp
#include "sum.h"
#include "a.h"
#include "b.h"
#include <iostream>
int main() {
    std::cout << af() + bf();
}

And this works fine as expected. Then I use define the sum function to be inline in sum.cpp and sum.h, and it fails to compile:

"sum(int, int)", referenced from:
      bf() in b.cpp.o
      af() in a.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Can somebody clarify this for me?

Upvotes: 1

Views: 261

Answers (3)

Klaus
Klaus

Reputation: 25613

The include guard with #ifndef/#define/#endif or with #pragma once only prevents the inclusion for every single translation unit.

Lets assume you have a header like:

#pragma once

void Do() {}

and two *.cpp files which do the include. If you now compile with

g++ source1.cpp source2.cpp 

you will get a

multiple definition of Do() in xy line/file

Every source file will compiled "alone" and so the guard is not seen from the second translation unit which was set by the first one. Both translations ( compilations ) are done fully independent. As this, the include guard will not protect anything in this case.

For this it is possible to define a function definition as inline. Now both definitions will be presented to the linker but marked as "weak". The linker now did not complain about the two definitions and only takes one of them ( typically the last! ) which is not important, as both are the same in that case.

So the include guard has the sense, that you can include a file multiple times to one translation unit. This will happen typically only if you indirect include a header. Lets say a.h has the function definition and b.h and c.h each include a.h. If your cpp-file includes now b.h and c.h both of them include a.h. So you have, without include guard, multiple definitions. This is the use case for include guards.

And the opposite problem having a "undefined symbol" if the function is defined inline but not visible in all using translation units:

Example:

Having that file:

inline void sum() {}
void f1() { sum(); }

and compile with -O0 produces, output of nm f1.o|c++filt

0000000000000000 T f1()
0000000000000000 W sum()

We see the symbol for sum defined as weak, so it can be present multiple times in link stage. It will also be used if a second translation unit which did not "see" the definition, will be linked without problems.

But using "-O3" you get:

0000000000000000 T f1()

That is implementation specific to the compiler! A compiler can provide inlined functions. Typically they do not, if higher optimization levels are in use.

As a rule: If a function is defined inline, its definition must be visible to every translation unit before it is used there!

Upvotes: 5

R Sahu
R Sahu

Reputation: 206607

Then I use define the sum function to be inline in sum.cpp and sum.h, and it fails to compile:

When a function is declared inline, its definition must be available in all the translation units that use the function. Defining the function in only one translation unit is not correct. That's why you are seeing the linker errors.

Move the definition of the function to sum.h to resolve the linker errors.

Upvotes: 1

Slava
Slava

Reputation: 44258

You should read documentation carefully, beside the statement:

function included in multiple source files must be inline

it also says this:

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

which you violated in your example hense the error.

Upvotes: 0

Related Questions