Friends and Visual C++ Express 2005

T

t

Lippman's C++ Primer, 4th ed., p575 says:

Friendship is not inherited. Friends of the base class have no
special access to members of its derived class. If a base class is
granted friendship, only the base has special access. Classes derived
from that base have no access to the class granting friendship.

I tried this in Visual C++ 2005 Express, and it seems to only
implement half of the friendship rules above.

Example code:

=================================================



class A2;

class A
{
friend class B;
friend void g(A, A2);
private:
int x;
};

class A2 : public A
{
private:
int x2;
};

class B
{
public:
void f(A a) { a.x; }

void f2(A2 a2) { a2.x; a2.x2;}
// a2.x compiles, but a2.x2 gives compile error
// both statements in f2 should be compile errors ???
};

class B2 : public B
{
public:
// void f(A a) { a.x; } // gives compile error, as it should
};

void g(A a, A2 a2)
{
a.x; // compiles
a2.x; // compiles, but shouldn't ?
a2.x2; // doesn't compile
}

int main()
{
}

=================================================

Is Visual C++ 2005 Express not implementing friendship rules correctly?
 
B

Barry

t said:
Lippman's C++ Primer, 4th ed., p575 says:

Friendship is not inherited. Friends of the base class have no
special access to members of its derived class. If a base class is
granted friendship, only the base has special access. Classes derived
from that base have no access to the class granting friendship.

I tried this in Visual C++ 2005 Express, and it seems to only
implement half of the friendship rules above.

Example code:

=================================================



class A2;

class A
{
friend class B;
friend void g(A, A2);
private:
int x;
};

class A2 : public A
{
private:
int x2;
};

class B
{
public:
void f(A a) { a.x; }

void f2(A2 a2) { a2.x; a2.x2;}
// a2.x compiles, but a2.x2 gives compile error
// both statements in f2 should be compile errors ???
};

class B2 : public B
{
public:
// void f(A a) { a.x; } // gives compile error, as it should
};

void g(A a, A2 a2)
{
a.x; // compiles
a2.x; // compiles, but shouldn't ?
a2.x2; // doesn't compile
}

int main()
{
}

=================================================

Is Visual C++ 2005 Express not implementing friendship rules correctly?

the compiler is correct.

"friend of farther(A)" (B, g) is not necessarily friend of son(A2).
 
T

t

I'm not sure. There are lines in the code I gave involving "a2.x"
that are compiling that I don't think should be compiling. It seems
like the Visual C++ compiler is using more lenient friendship rules.

The example in Lippman is different. I'll just type it out.
See "// my comment:" for my comments below. All other comments
are Lippman's.

==================================================

class Base {
friend class Frnd;
protected:
int i;
};

// Frnd has no access to members in D1
class D1 : public Base {
protected:
int j;
};

class Frnd {
public:
int mem(Base b) { return b.i; } // ok: Frnd is friend to Base
int mem(D1 d) { return d.i; } // error: friendship doesn't inherit
// my comment: VC++ compiles this!
};

// D2 has no access to members in Base
class D2 : public Frnd {
public:
int mem(Base b) { return b.i; } // error: friendship doesn't
inherit
// my comment: VC++ flags this as error!
};

=========================================================
 
I

Ivan Vecerina

: Lippman's C++ Primer, 4th ed., p575 says:
:
: Friendship is not inherited. Friends of the base class have no
: special access to members of its derived class. If a base class is
: granted friendship, only the base has special access. Classes derived
: from that base have no access to the class granting friendship.
:
: I tried this in Visual C++ 2005 Express, and it seems to only
: implement half of the friendship rules above.
:
: Example code:
:
: =================================================
:
:
:
: class A2;
:
: class A
: {
: friend class B;
: friend void g(A, A2);
: private:
: int x;
: };
:
: class A2 : public A
: {
: private:
: int x2;
: };
:
: class B
: {
: public:
: void f(A a) { a.x; }
:
: void f2(A2 a2) { a2.x; a2.x2;}
: // a2.x compiles, but a2.x2 gives compile error
: // both statements in f2 should be compile errors ???
a2.x is ok, without "inheritance of friendship":
A is a *public* base class of A2, so a2 can be used
as an object of type A (without friendship). And
because B is a friend of A, B::f2 has access to A::x.

: };
:
: class B2 : public B
: {
: public:
: // void f(A a) { a.x; } // gives compile error, as it should
Right: this is what demonstrates the limitation
described in Lippman's paragraph quoted above.

: };
:
: void g(A a, A2 a2)
: {
: a.x; // compiles
: a2.x; // compiles, but shouldn't ?
Again: seeing that a2 is of type A does not require friendship,
because A is a public base class of A2.

: a2.x2; // doesn't compile
: }
:
: int main()
: {
: }
:
: =================================================
:
: Is Visual C++ 2005 Express not implementing friendship rules
correctly?

The above code at least seems to be handled correctly.


hth -Ivan
 
I

Ivan Vecerina

: I'm not sure. There are lines in the code I gave involving "a2.x"
: that are compiling that I don't think should be compiling. It seems
: like the Visual C++ compiler is using more lenient friendship rules.
:
: The example in Lippman is different. I'll just type it out.
: See "// my comment:" for my comments below. All other comments
: are Lippman's.
:
: ==================================================
:
: class Base {
: friend class Frnd;
: protected:
: int i;
: };
:
: // Frnd has no access to members in D1
: class D1 : public Base {
: protected:
: int j;
: };
:
: class Frnd {
: public:
: int mem(Base b) { return b.i; } // ok: Frnd is friend to Base
: int mem(D1 d) { return d.i; } // error: friendship doesn't inherit
Well, my understanding would be that this is a typo in the book,
or in your transcription of it.
The code was probably meant to be:
int mem(D1 d) { return d.j; } // error: friendship doesn't inherit

: // my comment: VC++ compiles this!
: };
:
: // D2 has no access to members in Base
: class D2 : public Frnd {
: public:
: int mem(Base b) { return b.i; } // error: friendship doesn't
: inherit
: // my comment: VC++ flags this as error!
: };

hth --Ivan
 
B

Barry

t said:
I'm not sure. There are lines in the code I gave involving "a2.x"
that are compiling that I don't think should be compiling. It seems
like the Visual C++ compiler is using more lenient friendship rules.

The example in Lippman is different. I'll just type it out.
See "// my comment:" for my comments below. All other comments
are Lippman's.

Well, use the former example rather than this one
void g(A a, A2 a2)
{
a2.x; // compiles, but shouldn't ?
}

here a2.x is can be interpreted this way:

int A::*pm = &A::x; // (1)
a2.*x; // (2)

then the access control actually takes place on (1),
since g is a friend of A, then taking a private pointer to member of A
is legal.

So, to conclude, when we do inheritance, the members of the base class
are not members of the derived class. The derived class only *inherits*
them.
 
T

t

:
: class Frnd {
: public:
: int mem(Base b) { return b.i; } // ok: Frnd is friend to Base
: int mem(D1 d) { return d.i; } // error: friendship doesn't inherit
Well, my understanding would be that this is a typo in the book,
or in your transcription of it.
The code was probably meant to be:
int mem(D1 d) { return d.j; } // error: friendship doesn't inherit

It must be a typo in the book then.
 
T

t

Well, use the former example rather than this one
void g(A a, A2 a2)
{
a2.x; // compiles, but shouldn't ?

}

here a2.x is can be interpreted this way:

int A::*pm = &A::x; // (1)
a2.*x; // (2)

then the access control actually takes place on (1),
since g is a friend of A, then taking a private pointer to member of A
is legal.

So, to conclude, when we do inheritance, the members of the base class
are not members of the derived class. The derived class only *inherits*
them.

Is there a good reason for friendship to be like this? That friends
of A have access to the A parts of subclasses of A. This doesn't seem
"natural" or "right" to me w/ the way my mental model of C++ has been
building up. Maybe it's because I don't understand the underlying
mechanics of C++. It seems like there are some linguistic rules in
play that don't fit well w/ my mental model (I tend to think
geometrically rather than algebraically).

Or is this one of those rules that have no good basis but I simply
have to remember?
 
T

t

Let me quote the rest of Lippman's section on "Friendship and
Inheritance" p575-6, excluding the code. It seems to contradict what
Barry and Ivan are telling me.

"Friendship is not inherited. Friends of the base class have no
special access to members of its derived class. If a base class is
granted friendship, only the base has special access. Classes derived
from that base have no access to the class granting friendship.

....

If a derived class wants to grant access to its members to the friends
of its base class, the derived class must do so explicitly. Friends
of the base have no special access to types derived from that base
class. Similarly, if a base and its derived types all need access to
another class, that class must specifically grant access to the base
and each derived class."
 
G

Greg Herlihy

Well, use the former example rather than this one
void g(A a, A2 a2)
{
a2.x; // compiles, but shouldn't ?

}

here a2.x is can be interpreted this way:

int A::*pm = &A::x; // (1)
a2.*x; // (2)

then the access control actually takes place on (1),
since g is a friend of A, then taking a private pointer to member of A
is legal.

So, to conclude, when we do inheritance, the members of the base class
are not members of the derived class. The derived class only *inherits*
them.

Actually, the members of a base class inherited by a derived class,
are in fact members of the derived class as well:

"Unless redefined in the derived class, members of a base class
are also considered to be members of the derived class."[§10/1]

So, in this example, B has two potential routes to get to A2::x -
either through A2::A2::x or via A2::A::x. Since B is a friend of A's,
it can reach A2::x through A::x (since the "x" in either A or A2 is
the same member). And whenever there is more than one ways to access a
member of a class, a C++ compiler must choose the one that grants the
most access - which in this case is through B's friend, A.

Greg
 
B

Barry

Greg said:
Well, use the former example rather than this one
void g(A a, A2 a2)
{
a2.x; // compiles, but shouldn't ?

}

here a2.x is can be interpreted this way:

int A::*pm = &A::x; // (1)
a2.*x; // (2)

then the access control actually takes place on (1),
since g is a friend of A, then taking a private pointer to member of A
is legal.

So, to conclude, when we do inheritance, the members of the base class
are not members of the derived class. The derived class only *inherits*
them.

Actually, the members of a base class inherited by a derived class,
are in fact members of the derived class as well:

"Unless redefined in the derived class, members of a base class
are also considered to be members of the derived class."[§10/1]

So, in this example, B has two potential routes to get to A2::x -
either through A2::A2::x or via A2::A::x. Since B is a friend of A's,
it can reach A2::x through A::x (since the "x" in either A or A2 is
the same member). And whenever there is more than one ways to access a
member of a class, a C++ compiler must choose the one that grants the
most access - which in this case is through B's friend, A.

You're right, I should've checked out the standard first.

#include <iostream>

struct A
{
void f() {

}
};

struct B : A
{
void g() {
std::cout << typeid(&B::f).name() << std::endl;
std::cout << typeid(&A::f).name() << std::endl;
}
};

int main()
{
B b;
b.g();
}

produced output by MSVC8:
void (__thiscall A::*)(void)
void (__thiscall A::*)(void)

I judged membership by this way. So I guess I was wrong.
 
I

Ivan Vecerina

:
: > Well, use the former example rather than this one
: > void g(A a, A2 a2)
: > {
: > a2.x; // compiles, but shouldn't ?
: >
: > }
: >
: > here a2.x is can be interpreted this way:
: >
: > int A::*pm = &A::x; // (1)
: > a2.*x; // (2)
: >
: > then the access control actually takes place on (1),
: > since g is a friend of A, then taking a private pointer to member of
A
: > is legal.
: >
: > So, to conclude, when we do inheritance, the members of the base
class
: > are not members of the derived class. The derived class only
*inherits*
: > them.
: >
: > --
: > Thanks
: > Barry
:
: Is there a good reason for friendship to be like this? That friends
: of A have access to the A parts of subclasses of A. This doesn't seem
: "natural" or "right" to me w/ the way my mental model of C++ has been
: building up. Maybe it's because I don't understand the underlying
: mechanics of C++. It seems like there are some linguistic rules in
: play that don't fit well w/ my mental model (I tend to think
: geometrically rather than algebraically).
:
: Or is this one of those rules that have no good basis but I simply
: have to remember?

Well, if a class D publicly derives from A, instances of D *want* to
behave as if they were of type A. Otherwise, the subclass can
use private inheritance, or containement. If an instance of D
*is* an instance of A, friends as well may treat it as an A.
This seems fully logical and intuitive to me.

However, we do not want friendship to be "viral", which is what
the restrictions are about: a subclass shall not benefit from
or suffer from the consequences of the friedships of its parents.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,740
Latest member
JudsonFrie

Latest Threads

Top