47

Why does class D compile, but class C does not?

class A
{
    public:
        A(int) {}
};

template <class T>
class B : private T // Note: private base class
{
    public:
       using T::T;
};

class C : public B<A>
{
    public:
        C() : B<A>(123) {}  // Error: 'class A A::A' is inaccessible
};                          //         within this context

using BA = B<A>;

class D : public BA
{
    public:
        D() : BA(123) {}  // OK
};

I tested with GCC, Clang and Visual C++, and they are all the same. Changing class B : private T to public T solves the problem. But why? (Note that the using T::T is public.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Barnett
  • 1,491
  • 12
  • 18

1 Answers1

43

Class A contains the injected-class-name A within its scope (that is, A::A refers to class A unless it happens to refer to the constructor).

Class B inherits this, so the name A within the scope of B refers to the injected-class-name A in scope of A. However, since A is a private base class of B, all names in scope of A are private within B.

Class C again inherits this, but it cannot access this A, since it is private within B. Hence the error. Note that the error is actually with using the name A in the construct B<A>.

Class BA doesn't have this problem, since the definition B<A> is not in the scope of any class, so the name A refers to the global name A and not to any injected-class-name. And of course, the name BA is public.

You can easily solve this by qualifying the name A in C:

class C : public B<A>
{
public:
  C() : B<::A>( 123 ) {}
};

Note that constructor inheritance has no effect there. The problem is with access to the class name A (injected in A and inherited in B and C), not with access to the constructor.

Smeeheey
  • 9,906
  • 23
  • 39
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    So, in other (poor) terms, the error is that it's trying to access the name `A` in the (let me say) _wrong_ namespace (that is, the class `B`), am I wrong? Chapeau. Really interesting. – skypjack Aug 23 '16 at 13:13
  • @skypjack Yes, that's it. Another way to put it is that the private, and hence inaccessible in-class name `A`, hides the (accessible) global name `A`. – Angew is no longer proud of SO Aug 23 '16 at 13:15
  • @Yvette Yep, _fixed_ the comment. I've said that those are poor words!! :-) – skypjack Aug 23 '16 at 13:16
  • @Angew It makes absolutely sense and it's trivial to see now that you pointed it out. Thank you. It's one of the most obvious thing indeed, but less easy to see. :-) – skypjack Aug 23 '16 at 13:18
  • 7
    So here is the short version of the problem then: `class A {}; class B : A {}; class C : B { A a1; ::A a2; };` So `a2` is OK, but `a1` not. I can't believe I never encountered this before... – Barnett Aug 23 '16 at 14:23
  • 1
    @Barnett Really, how often do you use private inheritance to start with? – Yakk - Adam Nevraumont Aug 23 '16 at 15:42
  • @Yakk, good point. Seems to me like it is best to avoid anyway. I am going to see if I can make my code work with a private member instead. – Barnett Aug 23 '16 at 16:05
  • The fact that **Name lookup** (and overload resolution) **takes place before access control.** is sometimes confusing. – PcAF Aug 23 '16 at 20:45