Присвоение указателю лямбда-функции указателю на другую лямбда-функцию

Я пытаюсь назначить указатель на лямбда-функцию для указания на другую лямбда-функцию. Код будет говорить сам за себя:

#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;
}

Так как типы возврата и аргумент обеих лямбда-функций одинаковы, почему я не могу выполнить это назначение?

Ответ 1

[expr.prim.lambda]/2:

Тип лямбда-выражения (который также является типом объект закрытия) представляет собой уникальный, неназванный тип неединичного класса - называется тип замыкания - свойства которого описаны ниже.

То есть два типа замыкания всегда полностью различны и не связаны друг с другом. Однако в вашем примере, поскольку ни один из обоих lambdas не захватывает ничего, их можно преобразовать в указатель функции типа void(*)().
Поэтому, возможно, вы хотели сделать

void (*l1p)() = l1;
l1p = l2;

Ответ 2

В качестве дополнения к Коломбо ответьте, вы можете разделить тип и lambda-to-function-pointer с помощью operator +:

auto l1p = +l1; // l1p is void (*)()
//         ^
l1p = l2;

Ответ 3

Удалите синтаксический сахар, который предоставляют нам лямбда-функции:

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;
}

Это не точная трансформация, выполняемая компилятором, но я думаю, что достаточно рассмотреть проблему:

Два лямбда имеют собственный тип, который полностью не связан с их аргументами, захваченными переменными или возвращаемым типом. И, конечно же, типы лямбдов не связаны друг с другом, поэтому вы, очевидно, не можете назначить указатель на один из адреса другого.


Решение в другом ответе с помощью указателя функции довольно приятно. Если бы у вас не было доктрины "вы только платите за нее, когда используете ее" для С++, мы могли бы сделать лямбда-функции, такие как

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

Но для того, чтобы иметь смысл, база должна быть полиморфной (имеет виртуальную функцию-член) и что вам не нужна стоимость, если вы не собираетесь делать то, что хотите.