10

From [class.access]/7 we have the following sentence:

Similarly, the use of A::B as a base-specifier is well-formed because D is derived from A, so checking of base-specifiers must be deferred until the entire base-specifier-list has been seen.

class A {
protected:
    struct B { };
};
struct D: A::B, A { };

See live example with clang. As a matter of fact, clang also complains about this snippet, where no deferment is necessary.

class A {
protected:
    struct B { };
};
struct D: A, A::B { };

Why does this code not compile?

PS: gcc and VS21013 don't compile the codes either.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
Belloc
  • 6,318
  • 3
  • 22
  • 52
  • I'm sorry. I believe the code should compile. – Belloc Jul 05 '15 at 19:14
  • What does the second snippet do? - something unexpected I guess. – WorldSEnder Jul 05 '15 at 19:14
  • @WorldSEnder As far as [class.access]/7 is concerned the two snippets are equivalent, that is, D is privately derived from A and A::B. – Belloc Jul 05 '15 at 19:16
  • first of all, struct B is protected and using it outside to inherit from it is kind of wrong –  Jul 05 '15 at 19:21
  • @nilo But [class.access]/7 seems to say that, as `D` is derived from `A`, it has access to `A`'s protected members. – Belloc Jul 05 '15 at 19:24
  • @nilo The whole point of `protected` is that derived classes *can* see the name. If you don't want the name to be visible outside of the defining class, there's the `private` keyword for that. –  Jul 05 '15 at 19:25

2 Answers2

8

This is simply a compiler bug. The normative text of the standard supports the example. The fact that multiple compilers have the same bug means this is part of the standard is tricky to get right.

There are open bugs about this for GCC and for clang. Note that a few related cases are actually subtle differences between C++03 and C++11, but as far as I can tell, not this one.

[class.access]/1.2 merely states

protected; that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see 11.4).

and 11.4 does not expand on this. You are using the name B in a class D derived from that class A. That's fine.

3

I believe this to be a bug with clang. Ideone doesn't accept the code either: http://ideone.com/uiFl9L:

class A {
protected:
struct B { };
};
struct D: A::B, A { };

I checked with gcc-5.1.0, gcc-4.9 and clang-3.7(rc2). The standard explicitly states this as well-formed (see question) thus the compilers are at fault.

The example clearifies [class.access]/6:

All access controls in Clause 11 affect the ability to access a class member name from the declaration of a particular entity, including parts of the declaration preceding the name of the entity being declared ...

This means, according to [class.access]/2 that a class has access to all base-classes, even before they are declared.

WorldSEnder
  • 4,875
  • 2
  • 28
  • 64
  • Examples aren't normative, and my first thought was actually that the normative text probably contradicts the example. It doesn't, but if it did, then the compilers would be right to reject the example. –  Jul 05 '15 at 19:41
  • @hvd, I added a reason other than "it's in the example" – WorldSEnder Jul 05 '15 at 19:46
  • The "including parts of the declaration preceding the name of the entity being declared" doesn't apply here. The entity being declared is `D`, and the class member name `B` appears after the `D`. But the part of your quote that you didn't bold might actually be a good argument. :) –  Jul 05 '15 at 19:49