philibertin
philibertin

Reputation: 33

Why defining classes in header files works but not functions

I have this little piece of code :

File modA.h
#ifndef MODA
#define MODA

class CAdd {
public:
    CAdd(int a, int b) : result_(a + b) { }

    int getResult() const { return result_; }

private:
    int result_;
};

/*
int add(int a, int b) {
    return a + b;
}
*/
#end
File calc.cpp
#include "modA.h"

void doSomeCalc() {
    //int r = add(1, 2);
    int r = CAdd(1, 2).getResult();
}
File main.cpp
#include "modA.h"

int main() {
    //int r = add(1, 2);
    int r = CAdd(1, 2).getResult();
    return 0;
}

If I understand well, we can't define a function in a header file and use it in different unit translations (unless the function is declared static). The macro MODA wouldn't be defined in each unit translation and thus the body guard wouldn't prevent the header from being copied in place of every #include "modA.h". This would cause the function to be defined at different places and the linker would complain about it. Is it correct ?

But then why is it possible to do so with a class and also with methods of a class. Why doesn't the linker complain about it ? Isn't it a redefinition of a class ?

Thank you

Upvotes: 2

Views: 2580

Answers (3)

R Sahu
R Sahu

Reputation: 206557

When member functions are defined in the body of the class definition, they are inline by default. If you qualify the non-member functions inline in the .h file, it will work fine.

Without the inline qualifier, non-member functions defined in .h files are compiled in every .cpp file that the .h file is included in. That violates the following rule from the standard:

3.2 One definition rule

3 Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; ...

You will get the same error if you define member functions outside the body of the class definition in a .h file and did not add the inline qualifier explicitly.

Upvotes: 5

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145204

If I understand well, we can't define a function in a header file and use it in different unit translations (unless the function is declared static)

That's incorrect. You can, and in the presented code CAdd::getResult is one such function.

In order to support general use of a header in multiple translation units, which gives multiple competing definitions of the function, it needs to be inline. A function defined in the class definition, like getResult, is automatically inline. A function defined outside the class definition needs to be explicitly declared inline.

In practical terms the inline specifier tells the linker to just arbitrarily select one of the definitions, if there are several.

There is unfortunately no simple syntax to do the same for data. That is, data can't just be declared as inline. However, there is an exemption for static data members of class templates, and also an inline function with extern linkage can contain static local variables, and so compilers are required to support effectively the same mechanism also for data.

An inline function has extern linkage by default. Since inline also serves as an optimization hint it's possible to have an inline static function. For the case of the default extern linkage, be aware that the standard then requires the function to be defined, identically, in every translation unit where it's used.

The part of the standard dealing with this is called the One Definition Rule, usually abbreviated as the ODR.

In C++11 the ODR is §3.2 “One definition rule”. Specifically, C++11 §3.2/3 specifies the requirement about definitions of an inline function in every relevant translation unit. This requirement is however repeated in C+11 §7.1.2/4 about “Function specifiers”.

Upvotes: 4

Brian Bi
Brian Bi

Reputation: 119044

Multiple translation units might need the definition of the class at compile time since it is not possible to know, for example, the types of members of the class (or even whether they exist) unless the definition of the class is available. (Therefore, you must be allowed to define a class in multiple translation units.) On the other hand, a translation unit only needs the declaration of a function because as long as it knows how to call the function, the compiler can leave the job of inserting the actual address of the function to the linker.

But this comes at a price: if a class is defined multiple times in a program, all the definitions must be identical, and if they're not, then you may get strange linker errors, or if the program links, it will probably segfault.

For functions you don't have this problem. If the function is defined multiple times, the linker will let you know. This is good, because it avoids accidentally defining multiple functions with the same name and signature in a given program. If you want to override this, you can declare the function inline. Then the same rule as that for classes applies: the function has to be defined in each translation unit in which it's used in a certain way (odr-used, to be precise) and all the definitions must be identical.

If a function is defined within a class definition, there's a special rule that it's implicitly inline. If this were not the case, then it would make it impossible to have multiple definitions of a class as long as there's at least one function defined in the class definition unless you went to the trouble of marking all such functions inline.

Upvotes: 4

Related Questions