18

After getting an answer to this question I discovered there are two valid ways to typedef a function pointer.

typedef void (Function) ();
typedef void (*PFunction) ();

void foo () {}

Function * p = foo;
PFunction  q = foo;

I now prefer Function * p to PFunction q but apparently this doesn't work for pointer-to-member functions. Consider this contrived example.

#include <iostream>

struct Base {
    typedef void (Base :: *Callback) ();
                        //^^^ remove this '*' and put it below (i.e. *cb)
    Callback cb;

    void go () {
        (this->*cb) ();
    }

    virtual void x () = 0;

    Base () {
        cb = &Base::x;
    }
};

struct D1 : public Base {
    void x () {
        std :: cout << "D1\n";
    }
};

struct D2 : public Base {
    void x () {
        std :: cout << "D2\n";
    }
};  

int main () {
    D1 d1;
    D2 d2;
    d1 .go ();
    d2 .go ();
}

But if I change it to the new preferred style: typedef void (Base :: Callback) () and Callback * cb, I get a compiler error at the point of typedef

extra qualification 'Base::' on member 'Callback'

Demo for error.

Why is this not allowed? Is it simply an oversight or would it cause problems?

Community
  • 1
  • 1
spraff
  • 32,570
  • 22
  • 121
  • 229
  • @Als now take the `*` away from typedef and you'll see what he means. – RedX Sep 15 '11 at 10:46
  • @Als, no it [doesn't compile](http://www.ideone.com/CoQmY). – iammilind Sep 15 '11 at 10:47
  • 1
    To be precise: `typedef void Function();` defines an alias to the type *function taking not arguments and returning nothing*, note that there is no *pointer* in the type there... That typedef defines the signature of a function, not a pointer to a function with that signature. – David Rodríguez - dribeas Sep 15 '11 at 10:51

3 Answers3

11

For non-member functions, a type such as typedef void(Function)() has several uses, but for member functions the only application is to declare a variable which holds a function pointer. Hence, other than a stylistic preference, there's no strict need to allow this syntax and it has been omitted from the standard.

Background

The :: is a scope resolution operator, and the syntax X::Y is reserved for static member access if X is a class type. So X::*Z was another syntax invented to define pointer-to-member.

Forget member-function for a while, just think about member-data, and see this code:

struct X
{
   int a;
};

int X::*pa = &X::a; //pointer-to-member
X x = {100}; //a = 100
cout << (x.*pa) << endl;

It defines a pointer-to-member-data, and the cout uses it to print the value of a of object x, and it prints:

100

Demo : http://www.ideone.com/De2H1

Now think, if X::pa (as opposed to X::*pa) were allowed to do that, then you've written the above as:

int X::pa = X::a; //not &X::a

Seeing this syntax, how would you tell if X::a is a static member or non-static member? That is one reason why the Standard came up with pointer-to-member syntax, and uniformly applies it to non-static member-data as well as non-static member-function.

In fact, you cannot write X::a, you've to write &X::a. The syntax X::a would result in compilation error (see this).


Now extend this argument of member-data to member-function. Suppose you've a typedef defined as:

typedef void fun();

then what do you think the following code does?

struct X
{
   fun a;
};

Well, it defines member a of type fun (which is function taking no argument, and returning void), and is equivalent to this:

struct X
{
   void a();
};

Surprised? Read on.

struct X
{
   fun a; //equivalent to this: void a();
};

void X::a() //yes, you can do this!
{
     cout << "haha" << endl;
}

We can use exactly the same syntax to refer to a which is now a member-function:

X x;
x.a(); //normal function call

void (X::*pa)() = &X::a; //pointer-to-member
(x.*pa)(); //using pointer-to-member

The similarity is the synatax on the right hand side : &X::a. Whether a refers to a member-function or member-data, the syntax is same.

Demo : http://www.ideone.com/Y80Mf

Conclusion:

As we know that we cannot write X::a on the RHS, no matter if a is a member-data or member-function. The only syntax which is allowed is &X::f which makes it necessary that the target type (on LHS) must be pointer as well, which in turn makes the syntax void (X::*pa)() absolutely necessary and fundamental, as it fits in with other syntax in the language.

spraff
  • 32,570
  • 22
  • 121
  • 229
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Argument for `static` member access is quite **reasonable**. However, one thing is still to be addressed that, for member function pointer syntax we enclose it in `()` which will distinguish it with the `static` members. – iammilind Sep 15 '11 at 11:12
  • @iammilind: I don't quite follow your comment... in the case of a member function, as in the case of a non-member function, you have to enclose with `()` which is funnily interpreted as enclosing *everything else other than what is inside the parenthesis* (niceties of the C declaration syntax) in the declaration, to distinguish the pointer to function from a function returning a pointer. I don't see where `static` members have anything to do with that set of parenthesis, or maybe I misunderstood your comment? – David Rodríguez - dribeas Sep 15 '11 at 11:20
  • @David, according to Nawaz's answer, `::` is reserved for `static` member access (when used with `class` at LHS). So it's difficult for compiler to interpret that `::` is meant for class member function pointer declaration. I agree with that. ..... However, it can be argued that member function pointer can be still distinguished by extra `()` we put across. E.g. `typedef void (Base :: Callback) ();` ... in this case we can still say that `Callback` should be meant for class members and not `static` members; because it's enclosed in `()`. – iammilind Sep 15 '11 at 11:25
  • I'm pretty sure your final section on `int X::pa=X::a;` isn't relevant because it neither makes sense in and of itself nor has an analogy in member functions. You're talking about *objects* not *types*. – spraff Sep 15 '11 at 11:31
  • @iammilind: `void base::f()` is equivalent to `void (base::f)()` in the current standard, and what you propose would force a change in semantics there. Note that parenthesis are meant for *grouping* of elements, rather than changing semantics --admitedly, there are other cases where parenthesis can be used to change the meaning of the expression, as in `type t(( int() ));` where the extra set of parenthesis forces the parsing to be the creation of a local `t` by calling the constructor that takes an `int`, rather than declaration of a function that returns `type`... – David Rodríguez - dribeas Sep 15 '11 at 11:50
  • @David, agree with you. In that case I don't have any argument with this answer (and already +1 this with my 1st comment). – iammilind Sep 15 '11 at 12:04
  • @iammilind: You said, `However, it can be argued that member function pointer can be still distinguished by extra () we put across`. That doesn't make any difference, because you can't write `X::f` on the RHS. The only syntax which is allowed is this : `&X::f` which makes it necessary that the target type must be *pointer* as well, which in turn makes the synax `void (X::*ptr)()` absolutely necessary and fundamental. – Nawaz Sep 15 '11 at 12:05
  • @spraff: And are you sure you know enough to judge answer of *your* question, the answer which you don't know in the first place? What is the point of allowing `void (X::fun)()` if you cannot use it? As the language doesn't allow `X::f` on the RHS, and the only syntax which allows is `&X::f` which is a *pointer*, which in turn makes it absolutely necessary that the *target* type must be a *pointer* of some form. – Nawaz Sep 15 '11 at 12:44
  • Here's the reason I don't accept your answer: from your conclusion " it necessary that the target type must be pointer as well". Yes. Fine. In the non-member case `PFunction x` is a pointer and `Function *x` is the same type of pointer. The question -- ***which you haven't answered*** -- is why the typedef style for `Function` does not work for analogous member functions whereas the style for `PFunction` carries over fine. – spraff Sep 15 '11 at 13:18
  • Funnily enough, your answer would be a very good answer, if only the question was different. :-) – spraff Sep 15 '11 at 13:20
  • 1
    @spraff: Because in case of non-member function, `Function` doesn't exist only for the syntax `Function *x`. It has other uses where PFunction **cannot** be used. One example is in my answer itself `struct X { fun a; };`. Trying using the typedef `void (*fun)()` here. It would not work. But in case of member function, there is no other use. So if the only is line : `MemFunction *x`, then why not make `MemFunction` is pointer itself, because `*` is a fundamental part of the syntax. – Nawaz Sep 15 '11 at 13:24
  • So you're saying it's not allowed purely because the alternative is sufficient for all use-cases? – spraff Sep 15 '11 at 13:31
  • @spraff: Exactly. I don't see any use of `MemFunctionType` without the `*`. If `MemFunctionType` is used along with `*`, then one thing pretty much clear that `MemFunctionType` **alone** is not completely meaningful in any circumstances. While in case of non-member function, `Function` has meaning on its own, and can be used where `PFunction` cannot be used! – Nawaz Sep 15 '11 at 13:33
  • Got there in the end. Thanks a lot, no, really, and I'm sorry this led to hostilities. I've taken the liberty of putting a summary at the top. Hope you don't find that arrogant too. – spraff Sep 15 '11 at 13:53
2

To be precise the two typedef's in the case of the non-member pointers are not the same:

typedef void function();
typedef void (*fptr)();

The first defines function as a function taking no arguments and returning void, while the second defines ftpr as a pointer to function taking no arguments and returning void. The confusion probably arises as the function type will be implicitly converted to a pointer type in many contexts. But not all:

function f;            // declares void f();
struct test {
   function f;         // declares void test::f()
};
void g( function f );  // declares g( void (*f)() ): function decays to pointer to function in declaration
g( f );                // calls g( &f ): function decays to pointer to function
void f() {}            // definition of f
// function h = f;     // error: cannot assign functions
function *h = f;       // f decays to &f
BenMorel
  • 34,448
  • 50
  • 182
  • 322
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Your `remove_ptr` doesn't work: http://ideone.com/mkifj I guess this means a pointer to type `void()` is not the same as the type `void(*)()` ?! – spraff Sep 15 '11 at 11:25
  • 1
    @spraff: You are right, the `remove_ptr` does not seem to be able to remove the pointer from a *pointer to member*. Now that I think about it, it would be technically impossible to do that, as the *pointer to member* is implemented not as a real pointer but as a complex structure that contains different information: for a member variable it will contain an offset from the beginning of the object, for a non-virtual function it will be a pointer to the actual function, and for a virtual function it has to contain an offset from the vtable... I will remove that part of the answer. – David Rodríguez - dribeas Sep 15 '11 at 11:44
0

Let's skip the "function" part for a second. In C++, we have the int, the int* and the int Foo::* types. That's a regular integer, pointer to integer, and a pointer to an integer member. There is no fourth type "integer member".

Exactly the same applies to functions: there's just no type "member function", even though there are function types, function pointer types, and member function pointer types.

MSalters
  • 173,980
  • 10
  • 155
  • 350