class A {};

class B : private A {};

class C : private B
{
public:
    class D : private A {}; // Error here
};

This code gives the following error (in VS 2013):

nested.cpp(8) : error C2247: 'A' not accessible because 'B' uses 'private' to inherit from 'A'

It gets fixed if I change the definition of D like this:

class D : private ::A {};

Is this correct behavior, and if so why?

At first I thought it was because C inherits privately from B which would hide the base classes. But if I eliminate the "middle man" class B and just use this:

class A {};

class C : private A
{
public:
    class D : private A {};
};

The error goes away.

share|improve this question
1  
The compiler is telling you why. because 'B' uses 'private' to inherit from 'A', not because C inherits privately from B. – n.m. 14 hours ago
    
@n.m. Well that doesn't tell me much. After all, I'm using a class from the global namespace and not a member of something. Anyway I get it now thanks to the answers. – user1610015 13 hours ago
    
Names are searched from inside out. When a name is found, the search stops. Then accessibility is checked. In your case the search path is C>B>A. B>A is blocked because of private inheritance, A is inaccessible. Which is what the compiler tells you. That A also exists in the global scope, and it's the same A, is irrelevant. – n.m. 13 hours ago
    
@n.m. Nope, the compiler didn't tell me all of that... – user1610015 13 hours ago
up vote 14 down vote accepted

Quote from cppreference:

A name that is private according to unqualified name lookup, may be accessible through qualified name lookup

With that in mind lets see how the unqualified name lookup would work for the first example:

class A {};

class B : private A {};

class C : private B
{
public:
    class D : private A {}; // Error here
};
  1. A is looked up within the scope of C. Had it been defined there, there would be no problem.
  2. It figures out A is privately inherited by it's base (private) class B and hence throws a compiler error. Clang says:
    note: constrained by private inheritance here:
    class B : private A {}; 

Again, as per the quote it should work, if you use fully qualified name, like you have shown

class D : private ::A {};

And as for your last example:

class A {};

class C : private A
{
public:
    class D : private A {};
};

It works because, the name lookup works for all names that are part of the same class. To quote from cppreference again:

All members of a class (bodies of member functions, initializers of member objects, and the entire nested class definitions) have access to all the names to which a class can access.

share|improve this answer
    
Thanks you really made it clear (and skypjack too). – user1610015 13 hours ago
    
It probably figures out? An answer should be an answer, not another underlying question. – skypjack 13 hours ago

It's a matter of scopes during name lookup:

  • When you use ::A it's a fully qualified name, thus you are explicitly referring to the global namespace and picking A up from there.

  • When you inherit from A, C (let me say) sees A and you can directly refer to the A name within C with an unqualified name.

  • When you inherit from B, C sees B and A is private in its scope. It's private, but it exists. Because A is an unqualified name and is looked for first of all in that scope, it happens to be found and unaccessible, thus the error.

share|improve this answer

Example from cppreference:

class A { };
class B : private A { };
class C : public B {
   A* p; // error: unqualified name lookup finds A as the private base of B
   ::A* q; // OK, qualified name lookup finds the namespace-level declaration
};

With private inheritance, public and protected member of the base class become private members of the derived class.

class B : private A {};

class C : private B
{
public:
    class D : private A {}; // Error because all members of A is private to B so what 
    //would be the use of this private inheritance if you can't access any of A's member.
};

While

class D :private ::A {}; 

works because A's members are directly taken from global namespace, enabling D to have access to A's public and protected members.

share|improve this answer

A crucial part for understanding, which is not explicitly spelled out in the other answers, is the following:

The name of a class is injected into the classes' scope.

That is, if you have

class A {};

then you can refer to class A not only by the name ::A but also by the name A::A. Note that despite describing the same class, those are not the same name, as they are in different scopes.

Now when in the scope of A or a class deriving directly or indirectly from A, unqualified lookup will find A::A rather than ::A (unless A::A is itself hidden by yet another name).

In addition, unlike some other languages, C++ does not hide private names from scopes where you cannot access them, but uses access specifiers only as permission to use the name. Moreover, those permissions are bound to the name, not to the named entity (in this case, the class).

So in your code, on unqualified lookup of A the compiler finds the name C::B::A::A which hides the name ::A, and then checks the access permission and finds this name is private in the current context, as it is a name in the scope of C::B::A which cannot be accessed from within C since A is a private base class of B.

share|improve this answer

class D : private ::A {}; When you inherit from B, C sees B and A is private in its scope. It's private, but it exists. Because A is an unqualified name and is looked for first of all in that scope, it happens to be found and unaccessible, thus the error.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.