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?