Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning pointer to lambda function to pointer to another lambda function

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?

like image 793
syntagma Avatar asked Aug 31 '15 10:08

syntagma


3 Answers

[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;
like image 106
Columbo Avatar answered Nov 15 '22 13:11

Columbo


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;
like image 32
Quentin Avatar answered Nov 15 '22 12:11

Quentin


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.

like image 7
Daniel Jour Avatar answered Nov 15 '22 14:11

Daniel Jour