13

I am trying to assign a pointer to lambda function to pointer to another lambda function. The code will speak for itself:

#include <iostream>

int main(int argc, char *argv[]) {

    auto l1 = []() { std::cout << "Lambda 1!" << std::endl; };
    auto l2 = [] { std::cout << "Lambda 2!" << std::endl; };

    auto l1p = &l1;
    l1p = &l2; // Why can't I do this assignment?
    return 0;
}

Since the return types and argument of both lambda functions are the same, why can't I do this assignment?

syntagma
  • 23,346
  • 16
  • 78
  • 134

3 Answers3

17

[expr.prim.lambda]/2:

The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type — whose properties are described below.

That is, two closure types are always entirely distinct and unrelated. However, in your example, as neither of both lambdas captures anything, they can be converted to a function pointer of type void(*)().
So perhaps you meant to do

void (*l1p)() = l1;
l1p = l2;
Columbo
  • 60,038
  • 8
  • 155
  • 203
  • In C++11(+) I would prefer to use `std::function l1f = l1;l1f = l2;` just as it easier to read. – Simon Kraemer Aug 31 '15 at 12:07
  • 8
    @SimonKraemer *please* don't. The overhead of `std::function` is not worth the slight syntactical improvement. Just use a `typedef`. – Quentin Aug 31 '15 at 12:08
  • @Quentin I just checked the compiler output. I am really surprised about the massive overhead. Thanks for pointing it out. – Simon Kraemer Aug 31 '15 at 12:13
  • @SimonKraemer `std::function`'s name really is not scary enough. It's not "a function", it's a type-erased functionoid container. I've heard of crazy compilers managing to optimize through it in some cases, but you probably don't want to rely on it ;) – Quentin Aug 31 '15 at 12:17
  • @Quentin The name is definately misleading. I thought it would just be a typesafe wrapper for function pointers (e.g. lambdas) that makes the usage of such more convenient.... – Simon Kraemer Aug 31 '15 at 12:26
  • 2
    @SimonKraemer it is a typesafe wrapper ... for *any* callable. A function pointer, a hand written class with an operator(), a lambda, etc. This may require heap allocation (to store state) (some implementions have a small object optimization), but does involve a vtable or equivalent for dispatching. The call overhead is similar to a virtual function call at `()`, which is slightly more than a function pointer call, but harder to inline. – Yakk - Adam Nevraumont Aug 31 '15 at 13:22
9

As an addition to Columbo's answer, you can have both type deduction and lambda-to-function-pointer decaying by using operator + :

auto l1p = +l1; // l1p is void (*)()
//         ^
l1p = l2;
Community
  • 1
  • 1
Quentin
  • 62,093
  • 7
  • 131
  • 191
7

Let's remove the syntactic sugar that the lambda functions provide us:

struct lambda1 {
  void operator () {
    std::cout << "Lambda 1!" << std::endl;
  }
};
struct lambda2 {
  void operator () {
    std::cout << "Lambda 2!" << std::endl;
  }
};
int main(int argc, char *argv[]) {

    auto l1 = Lambda1{};
    auto l2 = Lambda2{};

    auto l1p = &l1; // l1p is a Lambda1 *
    l1p = &l2; // &l2 is a Lambda2 *
    return 0;
}

This isn't the exact transformation done by the compiler, but I think it suffices to examine the issue:

The two lambdas have each an own type, that is totally unrelated to their arguments, captured variables or return type. And of course, the types of lambdas are to each other unrelated, so you can obviously not assign a pointer to one from the address of the other.


The solution in the other answer with the function pointer is pretty nice. If there weren't a "you only pay for it when you use it" doctrine for C++, we could have made lambda functions like

struct SomeLambda
  : public LambdaBase<
        Capture<int>,
        Arguments<bool>,
        Return<void>> {
  // ...
};
/*
  [integer]
  (bool flag) {
    if (flag) cout << integer;
  }
*/

But for that to make any sense the base would need to be polymorphic (has a virtual member function), and that's a cost you don't need if you don't intend to do what you wanted.

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63