Reputation: 294
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?
Upvotes: 1
Views: 2495
Reputation: 1539
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
Upvotes: 0
Reputation: 238321
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.
Upvotes: 4
Reputation: 1040
You can use #pragma once or the more "traditional" include guard.
As you guessed, it should be present in each header file.
Upvotes: 0