23

Hello I have this code with a compiler error (error is from Microsoft Visual Studio 2008):

class B {
protected:
    int b;
};

class A : public B {
public:
    void foo() { &B::b; } 
// error C2248: 'B::b' : cannot access protected member declared in class 'B'
};

while this code is error free:

class B {
protected:
    int b;
};

class A : public B {
public:
    void foo() { &(B::b); }
};

The two snippets seem to me equivalent based on my knowledge of the precedence of operators, because :: has an higher precedence than & (see for example table 2 at page 137 of "JOINT STRIKE FIGHTER AIR VEHICLE C++ CODING STANDARDS FOR THE SYSTEM DEVELOPMENT AND DEMONSTRATION PROGRAM" )

But they are different... I think it is something related to "pointer-to-data-member" but I do not know how does it fit with the operators precedence.

Any explanation?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153

3 Answers3

13

In the first case you're taking the address of pointer-to-member B::b. Since such a pointer is NOT a member of the parent of A but a separate object, it can't access it via the protected mechanism.

In the SECOND case where it works you're asking for the address of the specific instance of b, qualifying it with its base class so that in the case of multiple inheritance the compiler would know which base class you mean. In this context the protected attribute is visible.

Note that this compiles:

class B
{
protected:
int b;
};

class A : public B
{
public:
void foo(){ &A::b; }  // Note here &A:: instead of &B::
};

As an added example it doesn't work for the same reason that the following (hopefully more familiar) code doesn't work:

class B
{
protected:
int b;
};

class A : public B
{
public:
void foo(const B* b_obj) { b_obj->b; }
};
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • 1
    it will also compile `&this->B::b;` – Gene Bushuyev Feb 16 '11 at 16:07
  • @Gene Bushuyev Right, that's exactly equivalent to the OP's second (working) example: Get the address of the parent's specific `b` member, not a general pointer-to-member. – Mark B Feb 16 '11 at 16:11
  • 2
    Any reference from the Standard that talks about the syntax and the difference? How this syntax `&(B::b)` means that `b` belongs to a specific instance of `A`? – Nawaz Feb 16 '11 at 16:12
  • @Nawaz: `B::b` is typical class disambiguation syntax that can be used to refer either to data members or methods. It's just the interaction with parentheses and `&` which are surprising. – Matthieu M. Feb 16 '11 at 16:27
  • 2
    @Matthieu: Yes. I understand what `B::b` means. I actually wanted to ask what is so special about parentheses that makes the difference? – Nawaz Feb 16 '11 at 16:31
  • @Nawaz, see Ise's answer, it seems to explain it very well. Turns out it's not a matter of precedence, but a "feature" of the grammar. – Sergei Tachenov Feb 16 '11 at 17:48
7

This is just a supplementation.
§5.3.1/2 says:

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. In the first case, if the type of the expression is “T,” the type of the result is “pointer to T.” ...
For a qualified-id, ... If the member is a non-static member of class C of type T, the type of the result is “pointer to member of class C of type T.”

According to §5.1/7, B::b comes under the qualified-id case, but (B::b) doesn't. So, compiler interprets it as an lvalue.

Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26
6

The differece between the two statements becomes more obvious when you try and return the value:

int*     foo()    { return &(B::b);}  // This is a pointer to an int


int A::* foo()    { return &B::b; }   // This is a pointer to a member of type int

What you want to do is access it via the A object:

int A::* foo()    { return &A::b; }   // This is a pointer to a member of type int

As from the A you are allowed to access it.
Accessing it via B like that is accessing it from outside and thus triggers the access specifiers.

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • This explains the difference better, in my opinion. +1 – Nawaz Feb 16 '11 at 16:20
  • "Accessing it via B like that is accessing it from outside and thus triggers the access specifiers." is not correct, I think. OP's second example proves that. Also, imagine A contains yet another attribute named `b` shadowing `B::b`. Then you could use `&(A::b)` and `&(B::b)` to differentiate them. – Tilman Vogel Feb 16 '11 at 16:38
  • @Tilman Vogel: I disagree. That is why I show above how the two are differentiated by what is actually being generated by the and operator. – Martin York Feb 16 '11 at 21:18