23

您好,我的代码存在编译器错误(错误来自 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'
};

虽然此代码没有错误:

class B {
protected:
    int b;
};

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

根据我对运算符优先级的了解,这两个片段在我看来是等效的,因为::它的优先级高于(例如,参见“用于系统开发和演示程序的联合攻击战斗机 C++ 编码标准&的第 137 页的表 2 )

但它们是不同的......我认为它与“指向数据成员的指针”有关,但我不知道它如何符合运算符优先级。

有什么解释吗?

4

3 回答 3

13

在第一种情况下,您正在获取指向成员的地址B::b。由于这样的指针不是父对象的成员,A而是一个单独的对象,因此它不能通过受保护的机制访问它。

在它起作用的第二种情况下,您要询问特定实例的地址b,并使用其基类对其进行限定,以便在多重继承的情况下编译器会知道您的意思是哪个基类。在这种情况下,受保护的属性是可见的。

请注意,这会编译:

class B
{
protected:
int b;
};

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

作为一个附加示例,它不起作用的原因与以下(希望更熟悉)代码不起作用的原因相同:

class B
{
protected:
int b;
};

class A : public B
{
public:
void foo(const B* b_obj) { b_obj->b; }
};
于 2011-02-16T16:05:11.797 回答
7

这只是一个补充。
§5.3.1/2 说:

一元 & 运算符的结果是指向其操作数的指针。操作数应为左值或限定 ID。在第一种情况下,如果表达式的类型是“T”,那么结果的类型就是“指向 T 的指针”。...
对于qualified-id,... 如果该成员是T 类型的C 类的非静态成员,则结果的类型是“指向T 类型C 的成员的指针”。</p>

根据§5.1/7,B::b属于qualified-id case,但(B::b)不是。因此,编译器将其解释为左值。

于 2011-02-16T17:07:43.983 回答
6

当您尝试返回值时,这两个语句之间的差异变得更加明显:

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

您要做的是通过 A 对象访问它:

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

从 A 开始,您可以访问它。
像这样通过 B 访问它是从外部访问它,因此会触发访问说明符。

于 2011-02-16T16:12:36.190 回答