Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Member Function as Friend: Is the book Lippman 5th wrong?

36 views
Skip to first unread message

Christiano

unread,
Mar 18, 2018, 7:36:16 PM3/18/18
to
Lippman 5th
ISBN-13: 978-0321714114

Page 280-281, it says:

Begin
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{

*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.

}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
End

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<A> x{A(10)};


Is there a correct way to do that??

Thank you!

Daniel

unread,
Mar 18, 2018, 10:46:35 PM3/18/18
to
To write

friend void B::hello();

B would need to be a complete type, a forward declaration for B wouldn't do.

But I suppose you could do something like this:

template <class T>
class A
{
friend void typename T::hello();

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

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

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

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

int main()
{
A<B> x(1);

return 0;
}

Daniel

Alf P. Steinbach

unread,
Mar 19, 2018, 1:57:32 AM3/19/18
to
For example as shown below.


> Is the book wrong?

Yes.


----------------------------------------------------------------------------
#include <stddef.h>
#include <vector>

using ScreenIndex = ptrdiff_t; // Not the silly
`std::vector<Screen>::size_type`
class Screen;

namespace window_mgr_screen_ops
{
void clear( Screen& );
};

class Screen
{
friend void window_mgr_screen_ops::clear( Screen& );
// ... rest of the Screen class

public:
Screen( int, int, char );
};

class Window_mgr
{
std::vector<Screen> screens{ Screen{ 24, 80, ' ' } };

public:
// reset the Screen at the given position to all blanks
void clear( ScreenIndex const i )
{
window_mgr_screen_ops::clear( screens[i] );
}
};
----------------------------------------------------------------------------


Cheers & hth.,

- Alf

Christiano

unread,
Mar 19, 2018, 7:11:07 AM3/19/18
to
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;
}

Conclusions about the book is wrong or not:

1- The book omits the necessity of firstly add a forward declaration of
class A .
2- The book omits that It is not possible to do in-class initialization
inside class B because class A must be incomplete (forward declaration)
at the time that class B is being defined.

Interesting facts:

1- vector<A> inside class B is not a problem (as long as A has been
forward-declared above)
2- A(10) inside class B is a problem because it is creating a object
with type A, which was not completely defined at that moment

Alf P. Steinbach

unread,
Mar 19, 2018, 7:39:02 AM3/19/18
to
A vector with items of incomplete type is permitted as of C++17.

It's worth being aware that that was not previously supported, i.e. this
code targets C++17 and above.

See <url:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4371.html>.
See note above.


> 2- A(10) inside class B is a problem because it is creating a object
> with type A, which was not completely defined at that moment


0 new messages