成员函数指针的奇怪C ++规则?

时间:2011-08-20 19:51:24

标签: c++ language-lawyer pointer-to-member

  

可能重复:
  Error with address of parenthesized member function

this recent question中,OP遇到了C ++语言的一个奇怪的规定,如果该成员函数名称被括起来,则取得成员函数的地址是非法的。例如,此代码是非法的:

struct X {
    void foo();
};

int main() {
    void (X::* ptr)();
    ptr = &(X::foo);   // Illegal; must be &X::foo
}

我查了一下,发现这是由于C ++ ISO规范的§5.3.1/ 3,其中包含

  

指向成员的指针仅在显式&使用它的操作数是一个未括在括号[...]

中的限定id

有没有人知道为什么规范有此规则?它特定于指向成员的指针,所以我怀疑这会解决一些语法模糊性,但老实说,我不知道它可能是什么。

2 个答案:

答案 0 :(得分:27)

这只是个人意见。 如果允许&(qualified-id)&(unary-expression), qualified-id必须是表达式,并且表达式应该具有类型 (即使它不完整)。 但是,C ++没有表示成员的类型,只有 指向成员的指针。 例如,以下代码无法编译。

struct A { int i; };

template< class T > void f( T* );

int main() {
  (void) typeid( A::i );
  f( &A::i );
}

为了使&(qualified-id)有效,编译器必须保持 内部成员类型。 但是,如果我们放弃&(qualified-id)表示法,则编译器不需要 处理成员类型。 由于成员类型总是以指向它的指针的形式处理, 我想标准优先考虑简化编译器的类型 系统一点。

答案 1 :(得分:3)

想象一下这段代码:

struct B { int data; };
struct C { int data; };

struct A : B, C {
  void f() {
    // error: converting "int B::*" to "int*" ?
    int *bData = &B::data;

    // OK: a normal pointer
    int *bData = &(B::data);
  }
};

如果没有括号的技巧,你将无法直接将指针指向B的数据成员(你需要基类类型转换和this的游戏 - 不太好)。


来自ARM:

  

请注意,必须明确使用address-of运算符来获取指向成员的指针;没有隐式转换......如果有,我们会在成员函数的上下文中产生歧义......例如,

void B::f() {
    int B::* p = &B::i; // OK
    p = B::i; // error: B::i is an int
    p = &i; // error: '&i'means '&this->i' which is an 'int*'

    int *q = &i; // OK
    q = B::i; // error: 'B::i is an int
    q = &B::i; // error: '&B::i' is an 'int B::*'
}

IS只保留了这个标准前的概念,并明确提到括号使得你不会得到指向成员的指针。