Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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.

like image 226
templatetypedef Avatar asked Aug 20 '11 19:08

templatetypedef


2 Answers

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.

like image 200
Ise Wisteria Avatar answered Oct 25 '22 11:10

Ise Wisteria


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.

like image 40
Johannes Schaub - litb Avatar answered Oct 25 '22 12:10

Johannes Schaub - litb