Access modifier's different behaviors in inheritance depend on "this" keyword and templates or lack thereof

Viewed 504

I want to understand the access modifiers' 4 different behaviors regarding inheritance when it comes to the 4 combinations of using and/or omitting templates and the this keyword. All following code is done in g++ 4.8:

Here's a GrandChild class, which privately inherits from Parent, which privately inherits from GrandParent, which has a public enum n. Non-object, client code can access GrandParent::n, because the latter is a public enum. But GrandParent::n is inaccessible from within GrandChild:

#include <iostream>
using namespace std;

struct GrandParent { enum {n = 0}; };

struct Parent : private GrandParent { enum {n = 1}; };

struct GrandChild : private Parent {
    enum {n = 2};
    void f() {cout << GrandParent::n << endl;}
    // ^ error: 'struct GrandParent GrandParent::GrandParent'
    // is inaccessible
};
int main() {
    cout << GrandParent::n << endl;
    // ^ non-object access would have outputted `0` had `GrandChild`'s
    // definition compiled or been commented out.
}

1.) Is GrandParent::n's inaccessibility from within GrandChild caused by GrandChild's possession of a GrandParent base subobject, which hides non-object access to GrandParent::num, and whose 2-generational privateness makes the base subobject’s n also inaccessible? I'd expected the error message to be about that.

2.) But apparently, it isn't. Why does the error complain about GrandParent's constructor?

3.) Prepending this-> to GrandParent::n in f()'s definition will add the error I expected in #1 but won't remove the ctor complaint. Why? I assumed that including this-> is redundant and that its omission will cause the lookup to attempt to find the n of the GrandParent subobject within GrandChild's scope before the less-immediately-scoped non-object n anyway.

4.) Why does this template variant compile? It seems functionally similar to the non-template one:

#include <iostream>
using namespace std;

template <unsigned int N>
struct bar : private bar<N - 1> {
    enum {num = N};
    void g() {
        static_assert(N >= 2, "range error");
        cout << bar<N - 2>::num << endl;
    }
};

template <>
struct bar<0> { enum {num = 0}; };

int main() {
    bar<2> b2;
    b2.g(); // Output: 0
}

5.) Prepending this-> to bar<N - 2>::num in g()'s definition causes the compiler error I expected in #1 only. But why doesn't it include the error of #2? And why doesn't its omission yield #2's error?

2 Answers
Related