32

Do expressions fun and &fun have the same type or not?

Consider the following code:

template <typename Check, typename T>
void check(T)
{
    static_assert(is_same<Check, T>::value);
}

void fun()
{}

check<void(*)()>(fun);
check<void(*)()>(&fun);

cout << typeid(fun).name() << endl;
cout << typeid(&fun).name() << endl;

Both assertions succeed which suggests that both expressions have the same type. However, typeids return different results:

FvvE
PFvvE

Why is that?

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
NPS
  • 6,003
  • 11
  • 53
  • 90

3 Answers3

30

Both assertions succeed because they are applied to the type T deduced from function argument. In both cases it will be deduced as a pointer to function because functions decay to a pointer to function. However if you rewrite assertions to accept types directly then first one will fail:

static_assert(is_same<void(*)(), decltype(fun)>::value);
static_assert(is_same<void(*)(), decltype(&fun)>::value);

online compiler

user7860670
  • 35,849
  • 4
  • 58
  • 84
18

fun and &fun refer to the same type because of function to pointer conversion, which is performed in check<void(*)()>(fun);; but typeid is an exception.

(emphasis mine)

Lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversions are not performed.

And why the function to pointer conversion is performed for check<void(*)()>(fun);, because in template argument deduction,

Before deduction begins, the following adjustments to P and A are made:

1) If P is not a reference type,

  • if A is an array type, ...;
  • otherwise, if A is a function type, A is replaced by the pointer type obtained from function-to-pointer conversion;

check() takes parameter by value, then function-to-pointer conversion is performed and the deduced type of T will be the function pointer too, i.e. void(*)().

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
7

When you use a function name as an expression, it decays to a pointer to itself. So fun will be the same as &fun.

As for the typeid thing, from this reference:

Lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversions are not performed.

[Emphasis mine]

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Why do `typeid`s return different type names? – NPS Jan 23 '18 at 10:09
  • @NPS Because `function-to-pointer conversions are not performed.` – Zereges Jan 23 '18 at 10:16
  • [conv.func]/1 says it _can_ ("An lvalue of function type T can be converted to a prvalue of type “pointer to T ”. The result is a pointer to the function."), but I cannot find a rule saying it _does_. – YSC Jan 23 '18 at 10:19
  • @YSC It *does* in template argument deduction; `check` takes argument by value and the deduced type `T` will be the function pointer. – songyuanyao Jan 23 '18 at 10:32
  • What does it mean "use as an expression"? When is that? When is it not? – NPS Jan 23 '18 at 15:48