Private inheritance question

J

Jonathan Potter

Hi, I was wondering if someone could explain something to me.
We recently upgraded to a new C++ compiler and found some old code wouldn't
compile.

Boiled down, the code that causes the error is:

class A
{
};

class B : private A
{
};

class C : public B
{
public:
// error here
void Func(A& a);
}


The error from the compiler is "'A' not accessible because 'B' uses
'private' to inherit from 'A'"

When I contacted the vendor's support about this I got the response:

"This is the way C++ works. The use of private inheritance turns all
non-private members of the base class into private members of the derived
class, and disallows all standard conversions between derived to base . This
construction

void Func:):A& a);

will work"

And he's right, it does work. I'm just wondering - what is the error in my
original construct? I can't see where the "conversion" that the response
refers to is. I'm not trying to convert anything from derived to base. I
simply have a function in the derived class to which I am passing a
parameter of the same type as the base class.

Thanks,
Jon
 
A

Alf P. Steinbach

* Jonathan Potter:
Hi, I was wondering if someone could explain something to me.
We recently upgraded to a new C++ compiler and found some old code wouldn't
compile.

Boiled down, the code that causes the error is:

class A
{
};

class B : private A
{
};

class C : public B
{
public:
// error here
void Func(A& a);
}


The error from the compiler is "'A' not accessible because 'B' uses
'private' to inherit from 'A'"

When I contacted the vendor's support about this I got the response:

"This is the way C++ works. The use of private inheritance turns all
non-private members of the base class into private members of the derived
class, and disallows all standard conversions between derived to base . This
construction

void Func:):A& a);

will work"

And he's right, it does work. I'm just wondering - what is the error in my
original construct? I can't see where the "conversion" that the response
refers to is.

You've focused on the wrong part of the answer -- that's just supplementary
information. The important part is that A is a private member of B. In C you
were trying to access that private member because lookup of the unqualified A
finds members first.
 
V

Victor Bazarov

Jonathan said:
Hi, I was wondering if someone could explain something to me.
We recently upgraded to a new C++ compiler and found some old code wouldn't
compile.

Boiled down, the code that causes the error is:

class A
{
};

class B : private A
{
};

class C : public B
{
public:
// error here
void Func(A& a);

'A' is inaccessible (private) base of 'B'. The name is visible, the
access is prohibited.
;
The error from the compiler is "'A' not accessible because 'B' uses
'private' to inherit from 'A'"

When I contacted the vendor's support about this I got the response:

"This is the way C++ works. The use of private inheritance turns all
non-private members of the base class into private members of the derived
class, and disallows all standard conversions between derived to base . This
construction

void Func:):A& a);

will work"

And he's right, it does work. I'm just wondering - what is the error in my
original construct?

'A' _inside_ 'C' refers to 'B::A', which is private.
> I can't see where the "conversion" that the response
refers to is. I'm not trying to convert anything from derived to base. I
simply have a function in the derived class to which I am passing a
parameter of the same type as the base class.

Name lookup for an unqualified name 'A' finds '::C::B::A' and prefers it
over '::A'.

V
 
J

Jonathan Potter

Ah! Thank you both. That makes perfect sense!

So actually the fact that this code used to compile in the old compiler
(Visual C++ 6.0) was presumably an error in the first place?
 
G

Greg

Victor said:
'A' is inaccessible (private) base of 'B'. The name is visible, the
access is prohibited.


'A' _inside_ 'C' refers to 'B::A', which is private.


Name lookup for an unqualified name 'A' finds '::C::B::A' and prefers it
over '::A'.

I would characterize the behavior slightly differently: the compiler
prefers "::C::B::A" because it finds it first. Once the C++ compiler
finds a declaration to match a name, it stops looking for any other
matching declarations, including declarations for the same symbol that
may offer a different level of access.

So whatever the access restriction is on the first declaration found,
is the access level that the compiler will apply to the name when it
appears in the source code. In this case the global namespace specifier
ensures that the compiler will find ::A first because it ensures that
the compiler will be looking only in the global namespace for A.

Greg
 
V

Victor Bazarov

Greg said:
[...]
I would characterize the behavior slightly differently: the compiler
prefers "::C::B::A" because it finds it first.

Yes and no. The compiler finds all of them. However, if a member with
that name exists, the other names are discarded (not considered). If
they were equally considered, there would be ambiguity (before checking
access specifiers). Of course, we could say that it finds the member
first (and stops looking for others) if we can prove (or assume) that the
compiler looks for members first.
> Once the C++ compiler
finds a declaration to match a name, it stops looking for any other
matching declarations, including declarations for the same symbol that
may offer a different level of access.

So whatever the access restriction is on the first declaration found,
is the access level that the compiler will apply to the name when it
appears in the source code. In this case the global namespace specifier
ensures that the compiler will find ::A first because it ensures that
the compiler will be looking only in the global namespace for A.

V
 
G

Greg

Victor said:
Greg said:
[...]
I would characterize the behavior slightly differently: the compiler
prefers "::C::B::A" because it finds it first.

Yes and no. The compiler finds all of them. However, if a member with
that name exists, the other names are discarded (not considered). If
they were equally considered, there would be ambiguity (before checking
access specifiers). Of course, we could say that it finds the member
first (and stops looking for others) if we can prove (or assume) that the
compiler looks for members first.

Only when searching for a function declaration is the compiler's search
exhaustive. For other types of names, including those in this example,
the compiler follows this rule:

"In all cases listed in 3.4.1, the scopes are searched for a
declaration in the order listed in each of the respective categories;
name lookup ends a soon as a declaration is found for the name. If no
declaration is found, the program is ill-formed." §3.4.1/1

I should clarify that the compiler when searching a particular scope
always searches that scope completely. In this way, names duplicated in
different declarations within the same scope prompt an error - while
names declared in one scope tend to "hide" duplicate names declared in
more distant scopes.

Greg
 

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,780
Messages
2,569,608
Members
45,252
Latest member
MeredithPl

Latest Threads

Top