redefinition of inline function in class

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.

 

 

 

 

 

 

Leave a Reply