0

I'm trying to make MainScheduler's method addJob a friend function of the class Job, as so:

#include "MainScheduler.h"
//forward declaration
class MainScheduler;

class Job:
{
    friend void MainScheduler::addJob( Job* const job );
    ...
}

But I keep getting the error

error C2027: use of undefined type 'MainScheduler'

Do you know why am I getting this message, and how can I fix it?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Idan
  • 5,365
  • 5
  • 24
  • 28
  • 5
    Why do you have a forward declaration for `MainScheduler` if you've already included `MainScheduler.h`? – Oliver Charlesworth Jun 02 '12 at 16:47
  • It didn't work before I wrote the forward declaration, with the following error: "error C2653: 'MainScheduler' : is not a class or namespace name". From what I've read in other peoples' questions about similar problems, adding a forward declaration solves the problem. So I thought that I should add a forward declaration. Only at my case, it just changed the error type. – Idan Jun 02 '12 at 16:53
  • The forward declaration is just masking the issue. The forward declaration doesn't tell the compiler that MainScheduler has a method called addJob, for example. There is probably a problem in MainScheduler.h – wreckgar23 Jun 02 '12 at 17:02
  • So, why the problem happened when I didn't have the forward declaration? In MainScheduler.h I have the method void addJob(Job* const job);. I don't see what can be the problem in MainScheduler.h. – Idan Jun 02 '12 at 17:18
  • Do the two header files use different include guards? Does MainScheduler.h also try to include Job.h? – aschepler Jun 02 '12 at 17:20
  • I had a problem of infinite inclusion, see second answer. – Idan Jun 02 '12 at 17:29

2 Answers2

5

You can only name a member function that has already been declared, even for a friendship declaration. This means the class must be defined, not just forward-declared.

Wrong:

class X;

class Y
{
    friend void X::f(); // ERROR
};

Right:

class X
{
public:
    void f();
};

class Y
{
    friend void X::f();
};

It's not obvious why your #include "MainScheduler.h" did not make the definition of class MainScheduler visible, so something else strange is going on there.

This rule means it is not possible to do something like A::f() is a friend of B and B::g() is a friend of A, so sometimes you just have to settle for friending an entire class. (Or there are fancy ways of using helper classes to request/grant permission for certain sets of functions, but that's more helpful when you need an extensible library interface.)

aschepler
  • 70,891
  • 9
  • 107
  • 161
2

The problem is quite simple: you cannot have dependency cycles.

// MainScheduler.h
#ifndef MAINSCHEDULER
#define MAINSCHEDULER

#include "Job.h"

class MainScheduler { friend class Job; };

#endif


// Job.h
#ifndef JOB
#define JOB

#include "MainScheduler.h"

class Job { friend class MainScheduler; };

#endif

What happens here when parsing MainScheduler.h is the following:

  • MAINSCHEDULER is not defined, thus the parsing starts
  • the preprocessor defines MAINSCHEDULER
  • the preprocessor includes Job.h
  • because MAINSCHEDULER is already defined, it skips the inclusion of MainScheduler.h
  • it includes the tokens from Job
  • includes of Job.h ends
  • it includes the tokens from MainScheduler

This yields the following preprocessor output, which the compiler sees:

// ignored #include "MainScheduler.h"

class Job { friend class MainScheduler; };

class MainScheduler { friend class Job; };

this is why before you introduced the forward declaration the compiler complained about the unknown MainScheduler symbol in the definition of Job.

Your headers cannot include themselves in a cycle, and you cannot befriend a member function with only a forward declaration.

I propose you rewrite Job.h as:

class MainScheduler; // forward declaration

class Job {
    friend class MainScheduler;
public:
    // whatever

};

By befriending the whole class you get away with just a forward declaration and break the cycle.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Wow, can't believe I did such a mistake. Anyway, there is something I don't understand - Why can't I befriend a member function with a forward declaration? (I should really try to avoid the friend class solution, as I am strictly asked to expose as little as possible and encapsulate as much as possible.) – Idan Jun 02 '12 at 17:28
  • @Idan: because to be able to befriend a method, this method must be declared. Since the class has not been defined (just forward declared), then its methods are unknown, and cannot be befriended. – Matthieu M. Jun 02 '12 at 21:03