1

Lippman 5th ISBN-13: 978-0321714114

Page 280-281, it says:

Making A Member Function a Friend

Rather than making the entire Window_mgr class a friend, Screen can instead specify that only the clear member is allowed access. When we declare a member function to be a friend, we must specify the class of which that function is a member:

class Screen {
    // Window_mgr::clear must have been declared before class Screen
    friend void Window_mgr::clear(ScreenIndex);
    // ... rest of the Screen class
};

Making a member function a friend requires careful structuring of our programs to accommodate interdependencies among the declarations and definitions. In this example, we must order our program as follows:

  • First, define the Window_mgr class, which declares, but cannot define, clear. Screen must be declared before clear can use the members of Screen.
  • Next, define class Screen, including a friend declaration for clear.
  • Finally, define clear, which can now refer to the members in Screen.

The problem is: class Window_mgr has a data member that depends of class Screen definition. See:

class Window_mgr {
public:
    // location ID for each screen on the window
    using ScreenIndex = std::vector<Screen>::size_type;
    // reset the Screen at the given position to all blanks
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{Screen(24, 80, ' ')};
};

So it is impossible firstly define Window_mgr without defining Screen previously! And at the same time, it is impossible define Screen without we have defined Window_mgr!!!

How can this problem be solved??? Is the book wrong?

I will paste here a code so that you can repeat the problem using a minimal code:

#include <iostream>
#include <string>
#include <vector>

class A
{
    friend void B::hello();

public:
    A(int i) : number{i} {}

private:
    void f() {
        std::cout << "hello" << std::endl;
    }
    int number;
};

class B {
private:
    std::vector<A> x{A(10)};

public:
    void hello()
    {
        for(A &elem : x)
        {
            elem.f();
        }
    }
};


int main()
{
    A x;

    return 0;
}

If I compile this code, the result is: error: use of undeclared identifier 'B' friend void B::hello();

And if I invert the position (A <--> B), I have: error: use of undeclared identifier 'A' std::vector x{A(10)};

Is there a correct way to do that??

Thank you!


EDIT:

Thank you, Craig Young

Solution:

#include <iostream>
#include <string>
#include <vector>

class A;

class B {
private:
    std::vector<A> x;

public:
    B();
    void hello();

};

class A
{
    friend void B::hello();

public:
    A(int i) : number{i} {}

private:
    void f() {
        std::cout << "hello" << std::endl;
    }
    int number;
};

B::B() : x{A(10)}
{

}

void B::hello()
{
    for(A &elem : x)
    {
        elem.f();
    }
}

int main()
{


    return 0;
}

Conclusion:

  • the book is incomplete in that it doesn't expose the necessity of doing the forward declaration of class A firstly and the impossibility to do in-class initialization in this case.
  • I didn't notice that the problem was the A(10), not the vector! That is, we can use incomplete type A (only declaration, without definition) when we are using it as Template argument to vector (because it doesn't create A object itself) but we can not use incomplete type A when defining a object, for example: A(10);

2 Answers2

1

You have to have an earlier declaration, but not an earlier definition.

Adding

class A;
class B;

at the front tells the compiler that “A” and “B” refer to classes. That should be enough for it to reason out the rest.

Bob Jacobsen
  • 1,150
  • 6
  • 9
1

For a start

Well, you're not following the guidance correctly.

First, define the Window_mgr class, which declares, but cannot define, clear. Screen must be declared before clear can use the members of Screen.

You must declare B before A.

Next, define class Screen, including a friend declaration for clear.

Now declare A with B::hello() as a friend.

Finally, define clear, which can now refer to the members in Screen.

B:hello() can use the private members of A.

This has been covered before here: C++ Forward declaration , friend function problem

You've added complications

Furthermore you want declarations of B to reference A. To achieve this you need to forward declare A so that B knows of its existence.

And it's important to be aware that you have only "partial" access to A. You cannot 'fully use' A in the declaration of B. So the following line in B is wrong.

//You're trying to create A when you only know it exists.
//You don't have a full definition of A yet.
std::vector<A> x{A(10)};

//Replace the above with...
std::vector<A> x;

Of course you'll have to find another way to initialise x.


Sample code

#include <iostream>
#include <vector>

class A;

class B
{
private:
    std::vector<A> x;
public:
    void hello();
};

class A
{
    friend void B::hello();
public:
    A(int i): number(i) {}

private:
    void f() { std::cout << "hello" << std::endl; }
    int number;
};

void B::hello()
{     
    for(A &elem : x)
    {
        elem.f();
    }
}

int main()
{
    A a{5};
    return 0;
}
Disillusioned
  • 14,635
  • 3
  • 43
  • 77