36

The following program's output seems to contradict itself:

#include <type_traits>
#include <iostream>
#include <functional>

void foo(int&){ std::cout << "called\n"; }

int main() {
    int a;
    foo(a);
    std::cout << std::is_invocable_v<decltype(foo), decltype(a)> << std::endl;
    std::invoke(foo, a);
}

The output is:

called
0
called

Which seems to me to be invoking a function that is not invocable? What is going on here?

Baruch
  • 20,590
  • 28
  • 126
  • 201

1 Answers1

62

decltype(a) is int. This corresponds to invoking f with an int prvalue -- something like f(7). That one indeed doesn't compile, because a non-const lvalue reference cannot bind to a prvalue.

What you're doing instead in main is calling f with an lvalue, a, to which the reference can bind just fine.

To get the correct result from std::is_invocable, use the expression form of decltype by adding parentheses:

std::is_invocable_v<decltype(foo), decltype((a))>
//                                          ^ ^
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 10
    Or use `decltype((a))` in this case :-S – Kerrek SB Jan 09 '18 at 11:40
  • @KerrekSB yes, definitely. I don't know why this escaped me. – Quentin Jan 09 '18 at 12:37
  • If `int` means a *prvalue* int, wouldn't that make `a` an *xvalue*, since it is a named *prvalue*? Or am I all confused about the nature of the value categories? – Baruch Jan 09 '18 at 12:59
  • 1
    @baruch Yes you are ;-) a is a simple lvalue. – papagaga Jan 09 '18 at 13:06
  • 4
    @baruch Value categories are properties of expressions, not properties of types. You can have expressions of type `int` that are lvalues (e.g. `a`), xvalues (e.g. `std::move(a)`) or prvalues (e.g. `a + 1`). – Barry Jan 09 '18 at 13:36
  • 1
    Xvalue, not prvalue. We're checking if we can invoke `f(declval())`. – Barry Jan 09 '18 at 13:38
  • @barry ooo, `struct no_move{ explicit no_move(int) {} no_move(no_move&&)=delete; }; void f(no_move){}` -- how can we test if `no_move` can be called with a prvalue, as opposed to an xvalue? `f( []()->no_move{ return no_move(1); }() )`. – Yakk - Adam Nevraumont Jan 09 '18 at 18:26
  • @Yakk isn't `f(no_move(1))` enough, or is there a point to the lambda? – Quentin Jan 09 '18 at 18:28
  • @Quentin True: I was thinking of ways to "transport" prvalues from one context to another, which does require something lambda like (as you cannot carry around a prvalue, but you can carry around something that when invoked produces a prvalue). After thinking it over, I've asked a SO question [here](https://stackoverflow.com/q/48174588/1774667) with your exact simplification. – Yakk - Adam Nevraumont Jan 09 '18 at 18:36