2

I have a class Angle with the << operator overloaded and two cpp files where the header file is included. I have a #ifndef statement to prevent the file from being included multiple times. However, it seems that it is included multiple times, as I got an error that operator << was defined multiple times. I then added a #warning satement to see when the file is included. In the compiler output it can be seen that the #warning is processed twice. If I move the definition to the cpp file it obviously works, but I still find the behavior in this case strange.

In my opinion the compiler shouldn't process the header file twice. Is there a reason for this behavior?

main.cpp

#include <iostream>
#include <cmath>
#include <cstdlib>
#include "Angle.h"
using namespace std;

int main() {
    Angle a = (Angle)(M_PI/4);
    cout << a << endl;
    return EXIT_SUCCESS;
}

Angle.h

#ifndef ANGLE_ANGLE_H
#define ANGLE_ANGLE_H
#include <iostream>

class Angle {
private:
    double value;
public:
    explicit Angle (double value) {this->value = value; }
    double getValue() const {return this->value;}

    // Operators
    operator const double() const {
        return this->value;
    }
};

#warning INCLUDING_ANGLE_H

std::ostream& operator << (std::ostream& os, const Angle& obj){
    os << obj.getValue();
    return os;
}
#endif //ANGLE_ANGLE_H

Angle.cpp

#include "Angle.h"

I'm compiling with the following command:

g++ main.cpp Angle.cpp -o angle

With the following error:

In file included from main.cpp:5:0:
Angle.h:19:2: warning: #warning INCLUDING_ANGLE_H [-Wcpp]
 #warning INCLUDING_ANGLE_H
  ^
In file included from Angle.cpp:1:0:
Angle.h:19:2: warning: #warning INCLUDING_ANGLE_H [-Wcpp]
 #warning INCLUDING_ANGLE_H
  ^
/tmp/cci53Hrd.o: In function `operator<<(std::ostream&, Angle const&)':
Angle.cpp:(.text+0x0): multiple definition of `operator<<(std::ostream&, Angle const&)'
/tmp/ccBbwtlD.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
ipa
  • 1,496
  • 1
  • 17
  • 36
  • 1
    This is a good time to read up on "translation units" and how C++ code is actually compiled. Basically, each .cpp file is compiled independently, then the linker's in charge up matching up function *definitions* (which can generally only be done once) with their *usages* that compiled based on the corresponding *declarations*. – Cameron Apr 23 '15 at 21:50
  • See also: http://stackoverflow.com/questions/14425262/why-include-guards-do-not-prevent-multiple-function-definitions – chris Apr 23 '15 at 21:51

2 Answers2

3

The problem is not that the header is included twice but rather that the function was defined twice. Define it in the .cpp file and add extern to the forward declaration in the header and everything should work. The problem is that each time the header is included it creates a new implementation of the function and you are getting a linker error that it doesn't know which one to use. That is why you shouldn't implement global functions in headers but rather in the .cpp.

Your Angle.h will have:

extern std::ostream& operator << (std::ostream& os, const Angle& obj);

And Angle.cpp will have:

std::ostream& operator << (std::ostream& os, const Angle& obj){
  os << obj.getValue();
  return os;
}
Benjy Kessler
  • 7,356
  • 6
  • 41
  • 69
  • Yes, I got that already as I wrote in my description. But I find it strange that it doesn't work the other way around. If I use a header only library I can include it in multiple cpp files as well without running into this problem. – ipa Apr 23 '15 at 22:01
  • The header file is indeed included twice, in Angle.cpp and main.cpp. Include guards only protect against including the same file twice in the same translation unit. This is usually fine because you are allowed to declare a variable many times. The problem comes in when you try to define a variable. That can only happen once. – Benjy Kessler Apr 23 '15 at 22:09
  • Ok, I wasn't aware of the handling of translation units. In this case it makes totally sense. Thanks – ipa Apr 23 '15 at 22:21
0

It's perfectly normal for a header file to be included multiple times when compiling. The #ifndef will only wall off multiple inclusions in the same compilation process, that is, the same .cpp file.

The compilation stage usually compiles multiple .cpp files down to an intermediate form that is later linked together into a single library or executable.

If you need to define the function once per target, that code has to be moved into a single .cpp file. Including it in the header may result in duplication.

tadman
  • 208,517
  • 23
  • 234
  • 262