Strange C++ rule for member function pointers? [duplicate]
Asked Answered
P

2

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.

Pape answered 20/8, 2011 at 19:51 Comment(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.Hamner
@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,Porthole
@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.Pape
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.Filagree
Yep, this is a duplicate question.Doak
@templatetypedef: It wasn't Troubador's question. Troubador has no questions.Filagree
"Does anyone have any idea why the spec has this rule?" Why wouldn't it? It's just syntax, so it's arbitrary.Polyzoarium
L
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.

Laurence answered 20/8, 2011 at 22:6 Comment(6)
+1 i think you're right. :-)Porthole
@AlfP.Steinbach: Wow! That's really encouraging :-DLaurence
Awesome! This seems like exactly the right answer. Thank you so much for posting this!Pape
@templatetypedef: Oh, not at all! Glad the answer helped :-)Laurence
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).Doak
@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.Laurence
D
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.

Doak answered 21/8, 2011 at 13:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.