3

Have a look at the code example listed below, I tested it with Compiler explorer (using gcc and clang) and it works and prints out the (expected) output of 200.

What I am trying to figure out: why exactly is this valid C++. Or is it not?

Here, I'm using the using keyword to define an alias (instead of a typedef) for the function type ft, which describes functions that take an int as argument and return an int (ft is a function type, not a function pointer type!). The equivalent typedef syntax is typedef int ft(int);. Since the C++ standard says that for every typedef there is an equivalent form using the using syntax, it is clear that using ft = int(int) is a well-defined way to specify this function type.

Now comes the interesting part: I use the type ft to specify the argument type of another function (print_it), and then call it, passing a lambda function. My question is: where/how does the C++ standard say that this should actually work? I think it is a wonderfully simple way to pass lambdas around. That this works is not clear to me, as a lambda is really a functor, not a function in the strict sense. So I think it is not clear that this lambda matches the type ft and therefore can be passed to print_it (if ft was defined to be std::function<int(int)> it would be clear though).

The code example:

#include <iostream>

using ft = int(int);

void print_it (ft f, int a)
{
    std::cout << f(a) << std::endl;
}

int main ()
{
    auto my_lambda = [] (int a) -> int { return 2 * a; };

    print_it (my_lambda, 100);

    return (0);
}
sperber
  • 661
  • 6
  • 20
  • 4
    `ft` decays to a function pointer and a non capturing lambda can be converted to a function pointer. – NathanOliver Jul 28 '22 at 22:29
  • @NathanOliver make that an answer, it's the exact answer to the question (I think). – lorro Jul 28 '22 at 22:30
  • 1
    related/dupe: https://stackoverflow.com/questions/28746744/passing-capturing-lambda-as-function-pointer and https://stackoverflow.com/questions/10997237/c11-lambdas-to-function-pointer – NathanOliver Jul 28 '22 at 22:30
  • OP is looking for a standards qoute though i think: `where/how does the C++ standard say that this should actually work?` – Fantastic Mr Fox Jul 28 '22 at 22:30
  • 1
    @lorro There should be plenty of duplicates. Found 2ish – NathanOliver Jul 28 '22 at 22:31
  • @NathanOliver: I have to look up the details, but the hint regarding the decay is the precise information I was missing to wrap my head around it. Thanks. – sperber Jul 28 '22 at 22:33
  • 1
    @sperber No problem. https://timsong-cpp.github.io/cppwp/dcl.fct#5 covers why `ft` becomes a function pointer – NathanOliver Jul 28 '22 at 22:34

2 Answers2

8

My question is: where/how does the C++ standard say that this should actually work?

This is specified in [expr.prim.lambda.closure]/7, which specifies that a lambda's closure type has a conversion function to a function pointer type matching the lambda's parameter and return types if the lambda is non-generic and doesn't have any capture. Calling through this function pointer basically behaves as if the lambda body was just a normal function to which the pointer points, which is possible because there are no captures which could give the lambda a state that a normal function can't have.

This applies here and you are using the conversion operator to implicitly convert the lambda object to a function pointer when passing it to print_it. This works since the lambda's parameter and return type matches the ft type and a function type used as type of a function parameter is adjusted to a pointer-to-function type instead. (See [dcl.fct]/5 for the last part.)

For generic lambdas there is a conversion function template (see the following standard paragraph). For lambdas with a capture there is no such conversion function and therefore this wouldn't work for them.

user17732522
  • 53,019
  • 2
  • 56
  • 105
1

This only works because your lambda does not capture. A capture-less lambda can be converted to a C-style function pointer but a captureing lambda cannot.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23