B
is allowed to access protected members of A
as long as the access is performed through an object of type B
. In your example you're trying to access foo
through A
, and in that context it is irrelevant whether B
derives from A
or not.
From N3337, §11.4/1 [class.protected]
An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class (11.2) As described
earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C
. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall denote C
or a
class derived from C
. All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C
or a class derived from C
. [Example:
class B {
protected:
int i;
static int j;
};
class D1 : public B {
};
class D2 : public B {
friend void fr(B*,D1*,D2*);
void mem(B*,D1*);
};
// ...
void D2::mem(B* pb, D1* p1) {
// ...
int B::* pmi_B = &B::i; // ill-formed
int B::* pmi_B2 = &D2::i; // OK
// ...
}
// ...
—end example]
Your example is very similar to the code in D2::mem
, which shows that trying to form a pointer to a protected member through B
instead of D2
is ill-formed.