22

Possible Duplicate:
Error with address of parenthesized member function

In this recent question the OP ran into a strange provision of the C++ language that makes it illegal to take the address of a member function if that member function name is parenthesized. For example, this code is illegal:

struct X {
    void foo();
};

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

I looked this up and found that it's due to §5.3.1/3 of the C++ ISO spec, which reads

A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses [...]

Does anyone have any idea why the spec has this rule? It's specific to pointers-to-member, so I would suspect that there is some grammatical ambiguity that this resolves, but I honestly haven't the faintest idea what it might be.

Community
  • 1
  • 1
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 7
    @Hans: Not a duplicate. OP is actually the highest-rated answer to that question, which asked _what_ the problem was. This question is asking _why_. – Nemo Aug 20 '11 at 22:31
  • 7
    @Troubadour: the WHY of something is different from the WHAT and HOW. for example, the what-question "what kind of trousers do Donald Duck wear?" has the answer "he's stark naked on lower part of body", but if you ask the why-question "why is Donald Duck stark naked on lower part of body", then it's a different answer, namely "he's a DUCK". To put it to a point, if you ask "what kind of trousers do Donald Duck wear", then it's **not** a valid answer to say "he's a DUCK". So as you can see, 2 diff q with 2 diff a. Cheers, – Cheers and hth. - Alf Aug 20 '11 at 22:41
  • 1
    @Troubador- I did not intend for this to be a duplicate of your question. My interpretation of your question was "what is the root cause of this problem" rather than "why is C++ structured this way," so I asked this question to get an answer to the latter question. I apologize if I misread your initial question and repeated it here. – templatetypedef Aug 21 '11 at 01:45
  • The original question **was** why. templatetypedef seems to have misinterpreted it as "what", which was silly, because the original question was "What is the reason behind not allowing parentheses while taking the address of a non-static member function?" This is very much a dupe. – Lightness Races in Orbit Aug 21 '11 at 13:48
  • Yep, this is a duplicate question. – Johannes Schaub - litb Aug 21 '11 at 13:49
  • 1
    @templatetypedef: It wasn't Troubador's question. Troubador has no questions. – Lightness Races in Orbit Aug 21 '11 at 13:55
  • "_Does anyone have any idea why the spec has this rule?_" Why wouldn't it? It's just syntax, so it's arbitrary. – curiousguy Nov 01 '11 at 05:25

2 Answers2

27

This is just a personal opinion. If &(qualified-id) is allowed as &(unary-expression), qualified-id has to be an expression, and an expression is expected to have a type (even if it is incomplete). However, C++ didn't have a type which denotes a member, had only a pointer to member. For example, the following code cannot be compiled.

struct A { int i; };

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

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

In order to make &(qualified-id) be valid, the compiler has to hold a member type internally. However, if we abandon &(qualified-id) notation, the compiler doesn't need to handle member type. As member type was always handled in the form of a pointer to it, I guess the standard gave priority to simplify the compiler's type system a little.

Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26
  • 6
    +1 i think you're right. :-) – Cheers and hth. - Alf Aug 20 '11 at 22:08
  • @AlfP.Steinbach: Wow! That's really encouraging :-D – Ise Wisteria Aug 20 '11 at 22:49
  • 2
    Awesome! This seems like exactly the right answer. Thank you so much for posting this! – templatetypedef Aug 21 '11 at 00:47
  • @templatetypedef: Oh, not at all! Glad the answer helped :-) – Ise Wisteria Aug 21 '11 at 09:14
  • 4
    Remember you can say `sizeof A::i` or `sizeof(A::i + 42)` in C++11. And you can call a non-static function with parenthesis wrapping the name: `struct A { void f() { } void g() { (f)(); } };` (`f` has type `void()` here). – Johannes Schaub - litb Aug 21 '11 at 13:53
  • @JohannesSchaub: Thank you for pointing out! In addition to what you commented, new standard seems to allow type-id like `int()const`. This looks like a non-member function type, but has cv-qualifier for `this`. If I were asked like so `This denotes a member (function) type, not a pointer to member type, right?`, I couldn't answer well. As I'm not so familiar with C++0x(11?), I wrote the answer with the past tense form. – Ise Wisteria Aug 21 '11 at 15:28
5

Imagine this code:

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);
  }
};

Without the trick with the parentheses, you would not be able to take a pointer directly to B's data member (you would need base-class casts and games with this - not nice).


From the ARM:

Note that the address-of operator must be explicitly used to get a pointer to member; there is no implicit conversion ... Had there been, we would have an ambiguity in the context of a member function ... For example,

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::*'
}

The IS just kept this pre-Standard concept and explicitly mentioned that parentheses make it so that you don't get a pointer to member.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212