Reputation: 93
I tested the following code(just for test, I know it's a bad idea), and I couldn't understand why it has an error. Please help me!
There are two classes A
and B
, both in separate hpp
files. I let them include each other(I think it doesn't matter because of the include guard
), and it didn't go wrong until I added the definition of B::fun
in B.cpp
.
A.h
#ifndef A_H
#define A_H
#include "B.h"
class A {
friend void B::fun(A&);
};
#endif
B.h
#ifndef B_H
#define B_H
#include "A.h"
class A;
class B {
public:
void fun(A&);
};
#endif
B.cpp
#include "B.h"
void B::fun(A& a) {
}
main.cpp
#include "A.h"
#include "B.h"
int main() {
}
I run it with command like this:
g++ -g -o test main.cpp B.cpp
Error message
A.h:7:17: error: 'B' has not been declared
friend void B::fun(A&);
I know two workarounds for the symptoms, but I want to understand the problems root cause.
Method 1:
comment the code #include "A.h"
in B.h
Method 2:
Modify #include "B.h"
to #include "A.h"
in B.cpp
I want to know why these workarounds avoid the symptoms, the exact reason for both methods.
Upvotes: 3
Views: 139
Reputation: 335
To understand where the error comes from, just follow the "chain of includes" that happen when you compile B.cpp
:
B.cpp
includes B.h
.B.h
includes A.h
.A.h
tries to include B.h
but doesn't, because you have correctly set up include guards.friend void B::fun(A&);
in A.h
, but at this stage B
has not been declared!Upvotes: 1
Reputation: 26753
You do not ask for a solution, you ask for the reasons that your two workarounds remove the error, good way of learning. My respect.
In order to understand why you get the error and why you can get rid of it with the two methods you found yourself, you should "play preprocessor" and do the including yourself, while observing the effect of your ifdef
s, aka reinclusion guards.
Looking at how B.cpp gets compiled, insert the header files where you do the includes.
You get:
#ifndef B_H
#define B_H
#ifndef A_H
#define A_H
#include "B.h" /* not expanded, will be removed next step */
class A {
friend void B::fun(A&);
};
#endif
class A;
class B {
public:
void fun(A&);
};
#endif
void B::fun(A& a) {
}
Then remove those parts which are prevented by the #ifdef
s, i.e. the second time of B header, you get:
#ifndef B_H
#define B_H
#ifndef A_H
#define A_H
class A {
friend void B::fun(A&); /* obviously unknown B */
};
#endif
class A;
class B {
public:
void fun(A&);
};
#endif
void B::fun(A& a) {
}
Now you see why you get the error in B.cpp.
Now why does method 1 work?
Lets do the same again, with that change applied:
#ifndef B_H
#define B_H
/* #include "A.h" */ /* method 1 */
class A;
class B {
public:
void fun(A&); /* fine, because forward declaration three lines above is sufficient */
};
#endif
void B::fun(A& a) {
}
Now, why does method 2 help?
/* method 2, include A.h before B.h into B.cpp */
#ifndef A_H
#define A_H
#ifndef B_H
#define B_H
/* #include "A.h" not expanded because of ifdef */
class A;
class B {
public:
void fun(A&); /* fine, because of forward declaration */
};
#endif
class A {
friend void B::fun(A&); /* fine of forward declaration B having been seen */
};
#endif
/* #include "B.h" not expanded, because of ifdef */
void B::fun(A& a) {
}
In short, both workarounds work by making the forward declaration be seen before using A
and B
being fully declared before being used.
Upvotes: 1
Reputation: 3165
Simply remove #include "A.hpp"
from B.hpp
. This is happening because when B.cpp
includes B.hpp
, the latter in turn includes A.hpp
before B
is defined. But you don't need to include A.hpp
at all because you have correctly foward-declared class A
.
Upvotes: 3