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?