5

My friend has shown me the follow code

struct A {
  virtual void f() = 0;
  virtual void g() = 0;
};

struct AInternal : A {
  virtual void f() { /* ... */ }
  virtual void g() { /* ... */ }
};

He is using AInternal as an internal class that implements most (if not all of A). He then inherited from AInternal, but as he wanted that AInternal stays inaccessible (since it is an implementation detail), he inherits protected (implemented in terms of). What he also did was usinging the base class name to make A accessible (it was protected by default, since AInternal was inherited protected too)

struct C : protected AInternal {
  using AInternal::A;
};

Actually, this worked fine (but as we later found, it still kept the member functions private - just the base class was made public), but it only worked on GCC. It fails to make base A accessible. Any idea? We could even make it to break code that works on Clang

struct C : public AInternal {
protected:
  using AInternal::A;
};

C *c = 0;
A *a = c; // error on GCC!

Can someone help out please?

steveax
  • 17,527
  • 6
  • 44
  • 59
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • If I get it right then A defines the Interface that is to be provided by C. What I actually do not get is the idea behind the whole setup. It makes public methods in `AInternal` inaccessible if not present in `A` but one could just have such methods private in `AInternal` and inherit public in C. – Pixelchemist Jul 14 '13 at 18:14
  • @Pixelchemist the idea was to make `using AInternal::A` make the member functions public again. That did not work, but what it *did* do was making the base class `A` accessible. – Johannes Schaub - litb Jul 14 '13 at 18:30
  • Yeah but I don't get the reason for this sort of layout. Why not have the interface methods public in `AInternal`, use public inheritance and be fine? The "implementation details" like helper functions or other members can still be private in `AInternal`. – Pixelchemist Jul 15 '13 at 03:04
  • @ainternal he wanted just the middle class to be protected. I guess a better way would have been to make a using declaration for class AInternal. But that fails because it would be an inheriting constructor declaration. – Johannes Schaub - litb Jul 15 '13 at 11:38

1 Answers1

5

You are only affecting the visibility of the injected-class-name. The access protection of the base subobject or its members should not be affected. If Clang or GCC allows it to affect a cast validity or access within the base, that's their bug.

[class.member.lookup] 10.2/3 says

In the declaration set, using-declarations are replaced by the members they designate, and type declarations (including injected-class-names) are replaced by the types they designate.

The base class subobject does not have a name in member lookup; the injected-class-name does.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Well, speak of the devil. I just hit a case in my own code where I incorrectly assumed that an injected-type-name had the same access qualification as the base class that introduced it. Turns out that if you really depend on it, it's better to explicitly define your own salted name, or use the traits idiom. – Potatoswatter Jul 15 '13 at 05:53
  • Thanks! I had no idea about all that and started to have bad sleep because of the base class visibility. +1 – Johannes Schaub - litb Jul 15 '13 at 11:40