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.