seineo
seineo

Reputation: 93

friend funciton error: 'B' has not been declared

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

Answers (3)

printf
printf

Reputation: 335

To understand where the error comes from, just follow the "chain of includes" that happen when you compile B.cpp:

  1. B.cpp includes B.h.
  2. B.h includes A.h.
  3. A.h tries to include B.h but doesn't, because you have correctly set up include guards.
  4. Now the compiler comes to the line friend void B::fun(A&); in A.h, but at this stage B has not been declared!

Upvotes: 1

Yunnosch
Yunnosch

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 ifdefs, 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 #ifdefs, 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

Peter
Peter

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

Related Questions