2

I've been told here that the differences between these two signatures is not lvalue/rvalue related.

#include <iostream>

template <typename RET_TYPE, typename...ARGs>
void takeFunction(RET_TYPE(*&& /*function*/)(ARGs...))
{
    std::cout << "RValue function" << std::endl;
}

template <typename RET_TYPE, typename...ARGs>
void takeFunction(RET_TYPE(*& /*function*/)(ARGs...))
{
    std::cout << "LValue function" << std::endl;
}

void function()
{
}

int main()
{
    void(*f)() = function;
    takeFunction(&function);
    takeFunction(f);
    return 0;
}

But if not that, then what is the difference that it is matching on?

Community
  • 1
  • 1
Adrian
  • 10,246
  • 4
  • 44
  • 110
  • Looks to me that the first is a reference to a function and the second is a reference to a function pointer. – Galik Apr 19 '15 at 06:57

2 Answers2

3

There's a difference between a function and a pointer to function.

Functions are different because they are not objects (in the standardese sense of the word). There is no such thing as function rvalues (outside of certain weird cases involving non-static member functions). In fact, rvalue references to function are lvalues whether named or not.

Pointers to functions are, well, pointers, which are objects. You can have a prvalue of pointer to function type, or an xvalue of pointer to function type, or an lvalue of pointer to function type. &function creates a prvalue pointer to function; in void (*f)() = function;, the function-to-pointer conversion is applied to convert the function lvalue function to a prvalue pointer to function, with which f is initialized.

Now consider this set of overloads:

template <typename RET_TYPE, typename...ARGs>
void takeFunctionRef(RET_TYPE(&& /*function*/)(ARGs...)) // #1
{
    std::cout << "RValue function" << std::endl;
}

template <typename RET_TYPE, typename...ARGs>
void takeFunctionRef(RET_TYPE(& /*function*/)(ARGs...)) // #2
{
    std::cout << "LValue function" << std::endl;
}

takeFunctionRef(function); // calls #2
takeFunctionRef(std::move(function)); // still calls #2!

Overload #2 is selected because of a special tiebreaker in [over.ics.rank], bullet 3.1.4 that favors binding an lvalue reference to a function lvalue over binding an rvalue reference to a function lvalue. But both will bind successfully (i.e., if you remove #2, you'll see #1 called in both cases).

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • So in the set of overloads you specified, only one is ever available if both exist (#2), but either may be used if declared without the other? I get that this isn't really ambiguous, but at the very least shouldn't it generate a warning that one will never be called? – Adrian Apr 19 '15 at 09:01
  • @Adrian Yes, but I suspect that this kind of situation don't really occur in real code nearly frequently enough to justify the cost of implementing a warning. – T.C. Apr 19 '15 at 09:16
  • 1
    "There is no such thing as function rvalues." - Well, if we're talking strictly according to the standard, I think there are some prvalues of function type, but their use is so carefully restricted that you simply can't encounter them in practice in a context where their value category actually matters. Considering `struct A { void f() { } }; A a;`, the expressions `A::f` and `a.f` are prvalues of function type according to [5.1.1p9] and [5.2.5p4.3.2], respectively. – bogdan Apr 19 '15 at 13:46
1

RET_TYPE(*&& /*function*/)(ARGs...) is an rvalue reference to a function pointer.

RET_TYPE(*& /*function*/)(ARGs...) is an lvalue reference to a function pointer.

&function creates an rvalue temporary function pointer. So takeFunction(&function); resolves to RET_TYPE(*&& /*function*/)(ARGs...).

void(*f)() = function; defines a named lvalue function pointer. So takeFunction(f); resolves to RET_TYPE(*& /*function*/)(ARGs...).

Besides this lvalue/rvalue difference, there is no other differences to these two functions.

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • And if I had a third `RET_TYPE(& /*function*/)(ARGs...)`, that would be a reference to a function, which is an lvalue? – Adrian Apr 19 '15 at 07:40
  • Correct. Also, I seem to recall that all rvalue references to function are practically resolved as lvalue references. In other words, you cannot really have an rvalue reference to function. Gonna find some standard reference to this. – Lingxi Apr 19 '15 at 07:48