We know an ordinary member function can not be defined multiple times. Suppose you have the following 3 files a.h, a.cpp, and main.cpp:
//a.h
#ifndef A_H
#define A_H
#include <iostream>
class A
{
public:
A();
void fun();
};
#endif // A_H
#include <iostream>
class A
{
public:
A();
void fun();
};
A::A()
{
std::cout << "A constructor";
}
void A::fun()
{
std::cout << "A::fun in a.cpp";
}
#include "a.h"
void A::fun()
{
std::cout << "A::fun in main.cpp";
}
int main()
{
A a;
a.fun();
return 0;
}
During building, you will meet the following linking errors:
error: multiple definition of `A::fun()’
collect2.exe:-1: error: error: ld returned 1 exit status
The error occurs even you do not call the fun, i.e. by commenting the “a.fun()” line in main.cpp. Both a.o and main.o are compiled out successfully, but the linker checks and finds the redefinition of fun in different translation units and reports the error.
If you change A::fun as an inline function in a.h, i.e.,
#ifndef A_H
#define A_H
#include <iostream>
class A
{
public:
A();
void fun()
{
std::cout<<"A::fun in a.h";
}
};
#endif // A_H
The situation is the same: the definition of fun in a.h and in main.cpp constructs the re-definition, which is found at compiling time and main.o cannot be generated. The definition of fun in a.cpp also forms the redefinition of fun in a.h, which is detected in linking time and both a.o and main.o can be generated. But if you redefine the inline function in a.cpp as follows:
//a.h
#ifndef A_H
#define A_H
#include <iostream>
class A
{
public:
A();
void fun()
{
std::cout<<"A::fun in a.h";
}
};
#endif // A_H
//a.cpp
#include <iostream>
class A
{
public:
A();
void fun()
{
std::cout << "A::fun in a.cpp";
}
};
A::A()
{
std::cout << "A constructor";
}
#include "a.h"
int main()
{
A a;
a.fun();
return 0;
}
The program can compile and link without problem. The output is “A constructorA::fun in a.h”.
So it seems the inline function in a class can be redefined. You can redefine the inline function in the class or outside class. Altering a.cpp as follows is ok.
//a.cpp
#include <iostream>
class A
{
public:
A();
void fun();
};
A::A()
{
std::cout << "A constructor";
}
inline void A::fun()
{
std::cout << "A::fun in a.cpp";
}
But if you try to redefine A::fun in main.cpp, you will get the redefinition error again.
#include "a.h"
inline void A::fun()
{
std::cout << "A::fun in main.cpp";
}
int main()
{
A a;
a.fun();
return 0;
}
This is because when compiling a.cpp, the compiler cannot know which piece of A::fun to use to replace a.fun(). So a.o cannot be generated. It is the compiler not the linker that produces the redefinition error.
So, let’s remove the redefinition of A::fun in main.cpp. Now the inline function A::fun is defined twice, one in the header file, the other in a.cpp. Which version of the inline function is used for the call in main.cpp? Actually, the version in the header file is used. The insertion of the code of the inline function is done at compiling stage, not linking stage. During compiling, the compiler sees the definition provided by the header file and places the function code at the calling point. There is no linking for the function at the linking stage as there is not a call to that function in main.o at all.
What if you remove the function definition from the class in the header and put it in a.cpp(also as an inline function)?
//a.h
#ifndef A_H
#define A_H
#include <iostream>
class A
{
public:
A();
void fun();
};
#endif // A_H
//a.cpp
#include <iostream>
class A
{
public:
A();
void fun();
};
A::A()
{
std::cout << "A constructor";
}
inline void A::fun()
{
std::cout << "A::fun in a.cpp";
}
//main.cpp
#include "a.h"
int main()
{
A a;
a.fun();
return 0;
}
Now you will get the error:
error: undefined reference to `A::fun()’
collect2.exe:-1: error: error: ld returned 1 exit status
But I’ve defined A::fun in a.cpp, why could this happen? Well, if the compiler sees a function that is not defined yet, it will think the function is an ordinary function(even you put an inline keyword in front of the declaration of the function), and would be defined in some other obj file, so let the linker link against it. Later, the linker looks up .obj files but only for ordinary functions and ignores any inline function definition because it thinks the insertion of inline function should have been done at compiling phase. The result is although the inline function is defined in a.cpp, the linker cannot link against it and reports the above error. That explains why we should always define inline functions in header files not source files. That also explains why for multiple definitions of an inline function in the header file and the source file, the header file version is selected. That is because the selection happens at compiling stage not the linking stage. The linker just ignores the inline function definition. Note that the linker checks every normal function in .obj files for redefinition even the normal function is never called, and the linker never checks inline functions in .obj files for redefinition.
Permalink
The One Definition Rule (ODR) says a symbol can only be defined once.
The trick with inline is it allows multiple definitions of the same symbol to occur in different compilation units (.o files). But they have to be equals to follow the ODR. The definition of the function, regardless if its implementation is in the .h or .cpp, is done in the .o file (compilation unit). You can only have 1 definition per compilation unit.
But you can have the exact same definition of a given symbol defined in multiple object files, it this symbol is tagged as inline. The linker will then pick one of them (consider this choice to be random).
Putting the implementation code as inline in the header is a way to help the compiler optimize the calls to that function from where it is called. Otherwise putting inline on a function that lies in a cpp file is useless with modern compilers.
If you enable link time code generation / optimization, you don’t even need to bother to put your small functions as inline in the headers : the compiler will run again at link time as if all the source code was put in a single giant file, and will be able to optimize each call, regardless if the function is tagged as inline or not.