I want to understand the access modifiers' 4 different behaviors regarding inheritance when it comes to the 4 combinations of using and/or omitting template
s and the this
keyword. All following code is done in g++ 4.8:
Here's a GrandChild
class, which private
ly inherits from Parent
, which private
ly 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 private
ness 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?