MiP
MiP

Reputation: 5

friend declaration block an external function access to the private section of a class

I'm trying to force function caller from a specific class. For example this code bellow demonstrate my problem. I want to make 'use' function would be called only from class A. I'm using a global namespace all over the project.

a.h

#include "b.h"
namespace GLOBAL{

class A{
public:
    void doSomething(B);
}
}

a.cpp

#include "a.h"
using namespace GLOBAL;

void A::doSomething(B b){

    b.use();
}

b.h

namespace GLOBAL{

class B{
public:
    friend void GLOBAL::A::doSomething(B);
private:
    void use();
}

Compiler says:

‘GLOBAL::A’ has not been declared

‘void GLOBAL::B::use()’ is private

Can anyone help here ?

Thanks a lot,

Mike.

Upvotes: 0

Views: 578

Answers (3)

Goz
Goz

Reputation: 62333

The following compiles fine from a cpp file:

namespace GLOBAL
{
    class B;

    class A
    {
    public:
        void doSomething(B& b);
    };
};


namespace GLOBAL
{
    class B
    {
    public:
        friend void GLOBAL::A::doSomething(B&);
    private:
        void use()
        {
        }
    };
};

void GLOBAL::A::doSomething(B& b)
{
    b.use();
}

As best I can tell your problem arises from the fact that you include "b.h" from "a.h" that defines the B class before the A class is defined yet the B class makes a reference to the A class. So you have problems. However you can't forward declare an object of type B because you are copying via the stack. Hence why I use a reference to class B (as this doesn't require the B object to be known in advance).

Basically you have some fundamental problems with your structure that needs working out. You need to do some reading up on forward declarations and circular dependencies

Edit: Specifying purely that class A is a friend of B (rather than a specific function in A that references B) is actually a possibility as the friend definition provides a kind of forward declaration. As such the following code compiles:

namespace GLOBAL
{
    class B
    {
    public:
        friend class A;
    private:
        void use()
        {
        }
    };
};

namespace GLOBAL
{
    class A
    {
    public:
        void doSomething(B b);
    };
};

void GLOBAL::A::doSomething(B b)
{
    b.use();
}

As such in your code, originally posted, chaging the friend statement to, simply

friend class A;

should allow your code to compile.

Upvotes: 1

Loki Astari
Loki Astari

Reputation: 264729

This is because in the friend delcaration you are refering to a member of a class.

For this to work the compiler must already have seen the full definition of A.

a.h

// #include "b.h"   // remove this line it is not needed.
namespace GLOBAL{

class B; // Forward declare the class here.

class A{
public:
    void doSomething(B&); // Note: This should probably be a reference.
                          // Change to doSomething(B&); 
}
}

b.h

// Add this line it is needed for the friend declaration.
// To have a member as a friend we need the definition of 'A'
#include "a.h"

namespace GLOBAL{

class B{
public:
    friend void GLOBAL::A::doSomething(B&);
private:
    void use();
}

a.cpp

#include "a.h"
// Add this line to include the B definition so you know how to call use()
#include "b.h"
using namespace GLOBAL;

void A::doSomething(B& b){ // b should be a reference otherwise you are copying it.

    b.use();
}

Upvotes: 2

Pete Becker
Pete Becker

Reputation: 76523

Move #include "b.h" from a.h to a.cpp after the first #include directive. The compiler doesn't need to see the friend declaration until it's compiling the function that the declaration applies to.

Upvotes: 0

Related Questions