0

I'm learning C++ and I have encountered (and fix) what seems to be a very classic problem :

g++ main.cpp A.cpp B.cpp -o out
In file included from B.h:1,
                 from main.cpp:3:
A.h:1:7: error: redefinition of ‘class A’
    1 | class A {
      |       ^
In file included from main.cpp:2:

Which from a quick research (assuming I understood it correctly), happens because the #include operation is not "idempotent" (a term I discovered with this problem).

To illustrate my question I propose a minimal working example.

main.cpp

#include "A.h"
#include "B.h"
#include <iostream>

int main () {
  std::cout << "Hello world" << std::endl;
  A a;
  B b(a);
  return 0;
}

A.h

#include <iostream>

class A {
  public:
      void test();
};

A.cpp

#include "A.h"

void A::test () {
  std::cout << "test" << std::endl;
}

B.h

#include "A.h"

class B {
  public:
      B(A);
};

B.cpp

#include "B.h"
#include <iostream>

B::B(A a){
  a.test();
}

Compiling the program with g++ main.cpp A.cpp B.cpp or more specifically g++ -c main.cpp will fail with the error shown above.

I understand that the compiler transcludes the header of "A" twice when compiling main.cpp (once at main.cpp:1 and once again at B.h:1 during its own inclusing at main.cpp:2). Effectively, the compiler 'sees' the definition of class A twice and thinks we are defining A twice.

What I Fail to understand is the include guards:

To fix this, one may use the keyword: pragma once at the top of the file that is included more than once as such:

A.h fixed with #pragma once

#pragma once
#include <iostream>

class A {
  public:
      void test();
};

Allowing the program to compile nicely.

To me this suggest that I should start every header with #pragma once !!! Which can't be right is it? Is it common practice? If so, Is there a way to do that at compile time instead (as a flag for instance)?

If I don't, I may not use object A as member of class A nor pass it as argument to B (as in the constructor of B in my example) if such A and B could be used individually in another file; unless I add #pragma once reactively every time the problem pops up which seems "dirty" to me. Furthermore, I would not be able to share my sources with anyone in fear they encounter the situation with two of my objects without having to add pragma once in my files themselves.

What Am I missing? Is there a way to avoid the problem altogether?

Jason Aller
  • 3,391
  • 28
  • 37
  • 36
NRagot
  • 84
  • 8
  • 2
    It's pretty common practice where it is allowed. The alternative is `#ifdef` guards. Either way most headers need to be protected from being included multiple times. – François Andrieux Sep 01 '20 at 20:02
  • Note that `#pragma once` is not standard, and is not guaranteed to work on all environments. – chakaz Sep 01 '20 at 20:06
  • Note also that `#pragma once` doesn't work with circular dependencies. For that you still need the good old `#ifdef`. – rustyx Sep 01 '20 at 20:07
  • I recommend to use `#ifndef` header guards. The guy who created `#pragma once` also recommends header guards instead of the pragma, [link](https://stackoverflow.com/a/34884735/4641116). – Eljay Sep 01 '20 at 21:08

3 Answers3

4

To me this suggest that I should start every header with #pragma once !!! Wich can't be right is it?

It can be right. Although it may in theory be a slight exaggeration.

Is it common practice?

Yes, it is. It is fairly universal if we include the other option of using a macro header guard into same practice.

If so, Is there a way to do that at compile time instead (as a flag for instance)?

If you mean, is there a way to make the pre-processor treat every included file as if they contained the pragma whether they have any form of header guard or not then no, there is no way to do that in C++.

In theory, you could write your own pre-processor that does this. However, although such pre-processor would be relatively simple, I would still consider that an unnecessarily complicated solution in relation to the benefit.

unless I add #pragma once reactively every time the problem pops up which seems "dirty" to me.

Is there a way to avoid the problem altogether?

There is a simple way to pre-emptively solve this problem, which you already mentioned: Add the pragma or a traditional macro header guard on top of every header file. There is no need to wait for problems to pop up. Just do this, and your worries are gone.

eerorika
  • 181,943
  • 10
  • 144
  • 256
0

You can use #pragma once or the more "traditional" include guard.

As you guessed, it should be present in each header file.

Cyril Jouve
  • 780
  • 3
  • 14
0

In this example you provided:

#pragma once
#include <iostream>

class A {
  public:
      void test();
};

is not guaranteed to work on all environments. (Like other comments have mentioned.) As well as Circular Dependices.

However using two preprocessor directives: #ifndef and #endif (include guards) prevents the header file from accidentally being included more than once. The #ifndef A_H tells the preprocessor to look for a constant named A_H that has not been created with the #define directive.

Now if A_H constant has not been defined then these following lines will be included in the program:

  class A {
public:
    void test();
};

For example using ifndef and #endif:

#ifndef A_H
#define A_H

class A {
public:
    void test();
};
#endif
Geno C
  • 1,305
  • 3
  • 6
  • 22