20

If the following from the C++ FAQ Lite is true: "a function name decays to a pointer to the function" (as an array name decays to a pointer to its first element); why do we have to include the ampersand?

typedef  int (Fred::*FredMemFn)(char x, float y);
FredMemFn p = &Fred::f;

And not just:

typedef  int (Fred::*FredMemFn)(char x, float y);
FredMemFn p = Fred::f;

In the second case Fred::f is a function and can decay to a pointer to that function.

I hope this question is not that stupid.

Cedric H.
  • 7,980
  • 10
  • 55
  • 82

1 Answers1

22

Original answer:

because a member-function is not a function and a member-function-pointer is not a function-pointer. Therefore the rules of decay don't apply.

Also, there is function type in C++, but not member-function type. So you can use a function in places where a pointer-to-function is expected, but you can't use a member-function because there is no such thing, only pointer-to-member-function. f in your example is a function. On the other hand, Fred::f is... well, nothing.

Also, I would argue that "the name of a function can decay...". No, the name cannot do anything, an lvalue of function type can be implicitly converted to a pointer-to-function, and this is an identity conversion as far as overload resolution is concerned

Editing to clarify my answer:

Every expression in C++ has a type and value. A value of one type can occasionally be converted to a value of another type. These conversions are ranked so that to make one conversion better than another one mainly for function overload resolution.

One of the types of the conversions is called lvalue-to-rvalue conversion. When an lvalue appears in a context where an rvalue is required this conversion takes place. Usually this kind of conversion does nothing, for example:

int i = 4, j = 5;
i = j;

on the second line j is an lvalue, but an rvalue is needed here, so j is converted to an rvalue. But this is not an observable conversion, is it? But there are cases where the lvalue-to-rvalue conversion can be observed. That is, an lvalue of array of n T can be converted to an rvalue of type T* whose value is the address of the first element of the array and an lvalue of type "function with signature S" an rvalue of type "pointer to function with signature S" whose value is the function's address

That means that when we assign a function to a pointer-to-function the function lvalue is implicitly converted to its address.

void f() {}
void (*p) () = f; //f is converted to rvalue

f is an expression and has a type. f's type is void()

There is no such type in C++ as a member-function There are pointers-to-member-functions, but not member functions themselves. I am talking of course about nonstatic functions. Static functions work the same way as ordinary functions, that is, you don't have to write &X::f, instead you can write X::f Why? Because X::f has a type function and the abovementioned conversion takes place. If f is nonstatic, however, X::f is of type... what? Oh yeah, it doesn't have a type and therefore is not an expression and therefore doesn't have value and therefore that value cannot be converted to anything.

Quote from the standard: 5.3.1 clause 3 A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses. [Note: that is, the expression &(qualified-id), where the qualified-id is enclosed in parentheses, does not form an expression of type “pointer to member.” Neither does qualified-id, because there is no implicit conversion from a qualified-id for a nonstatic member function to the type “pointer to member function” as there is from an lvalue of function type to the type “pointer to function” (4.3). Nor is &unqualified-id a pointer to member, even within the scope of the unqualified-id’s class. ]

Hope this was clearer...

Karsten Koop
  • 2,475
  • 1
  • 18
  • 23
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • "There is function type in C++, but not member-function type." -> What exactly do you mean by that? Surely `FredMemFn` has a type? – fredoverflow Oct 24 '10 at 14:57
  • @FredOverflow: Yes, FredMemFn has a type and that type is "pointer-to-member, including of which class and of what signature". Every expression in C++ has a type. For example if f is a freestanding function void f(), the type of the expression (f) is void() which is a function type. On the other hand the expression Fred::f is not an expression because it doesn't have a type. But &Fred::f is an expression of type pointer-to-member blah blah... that's what I mean – Armen Tsirunyan Oct 24 '10 at 15:30
  • Doesn't the `&` operator require an expression as its operand? – fredoverflow Oct 24 '10 at 15:44
  • @FredOverflow: see my edit and a quote from the standard which makes an exception where the operand of & is a qualified-id – Armen Tsirunyan Oct 24 '10 at 16:00
  • 2
    Oh, I was wrong! I got convinced by the fact that `typeid(Fred::FredMemFn)` does not compile. Quote from the standard: The result of the unary `&` operator is a pointer to its operand. The operand shall be an lvalue or a *qualified-id*. If the operand is a *qualified-id* naming a non-static member `m` of some class `C` with type `T`, the result has type "pointer to member of class `C` of type `T`" – fredoverflow Oct 24 '10 at 16:02
  • 2
    I'm astonished that this answer only got 1 (my) upvote so far... it's a great answer! – fredoverflow Oct 24 '10 at 16:27
  • @ArmenTsirunyan @FredOverflow @Cedric: I'm sorry, but this answer is wrong. `Fred::f` is a valid **expression**, a _primary expression_. But it is **not** an _lvalue_ (since `f` is a nonstatic member function) and hence the _funtion-to-pointer_ conversion doesn't take place. In order to get a _pointer to non-static class members_ you must use the unary operator `&`. – MWid Sep 26 '13 at 08:33
  • @MWid: I am sorry, but you are wrong. `Fred::f` is not an expression. If it were an expression `typeid(Fred::f)` would compile. – Armen Tsirunyan Sep 26 '13 at 08:34
  • 2
    @ArmenTsirunyan No! The use of an _id-expression_ that denotes a nonstatic member function is only allowed in some limited situations. Please read §5.1/10 (for C++03) or §5.1/12 (for C++11). – MWid Sep 26 '13 at 08:40
  • I agree with @MWid that `Fred::f` is a primary expression, in particular an *id-expression* as listed in the grammar referred to by the previous comment. I don't think this expression is given a type anywhere in the standard and its use is explicitly limited in [\[expr.prim.id\]/2](https://timsong-cpp.github.io/cppwp/n4659/expr.prim.id#2) of the C++17 standard (final draft) and equivalently in all other standard versions, but I suggest you remove the claim that it is not an expression. – walnut Jan 13 '20 at 10:36