Подтвердить что ты не робот

Получение указателя функции на лямбда?

Я хочу иметь возможность получить указатель на функцию лямбда в С++.

Я могу сделать:

int (*c)(int) = [](int i) { return i; };

И, конечно же, следующие работы - даже если это не создает указатель на функцию.

auto a = [](int i) { return i; };

Но следующее:

auto *b = [](int i) { return i; };

Дает эту ошибку в GCC:

main.cpp: In function 'int main()':
main.cpp:13:37: error: unable to deduce 'auto*' from '<lambda closure object>main()::<lambda(int)>{}'
     auto *b = [](int i) { return i; };
                                      ^
main.cpp:13:37: note:   mismatched types 'auto*' and 'main()::<lambda(int)>'

Кажется произвольным, что лямбда может быть преобразована в указатель функции без проблемы, но компилятор не может вывести тип функции и создать указатель на нее с помощью auto *. Особенно, когда он может неявно преобразовать unique, lambda type в указатель функции:

int (*g)(int) = a;

Я создал небольшую тестовую кровать в http://coliru.stacked-crooked.com/a/2cbd62c8179dc61b, которая содержит приведенные выше примеры. Такое поведение аналогично для С++ 11 и С++ 14.

4b9b3361

Ответ 1

Это не удается:

auto *b = [](int i) { return i; };

потому что лямбда не является указателем. auto не допускает преобразований. Несмотря на то, что лямбда конвертируется в нечто, что является указателем, это не будет сделано для вас - вы должны сделать это сами. С литом:

auto *c = static_cast<int(*)(int)>([](int i){return i;});

Или с колдовством:

auto *d = +[](int i) { return i; };

Ответ 2

Особенно, когда он может неявно преобразовывать уникальный, лямбда-тип в указатель функции:

Но он не может преобразовать его в "указатель на функцию". Он может преобразовать его только в указатель на определенную сигнатуру функции. Это не будет выполнено:

int (*h)(float) = a;

Почему это не удается? Поскольку здесь нет действительного неявного преобразования от a до h.

Преобразование для lambdas - это не магия компилятора. В стандарте просто сказано, что тип замыкания лямбда для несобранных не-генерических lambdas имеет неявный оператор преобразования для указателей функций, соответствующих сигнатуре его перегрузки operator(). Правила инициализации int (*g)(int) из a допускают использование неявных преобразований, и, таким образом, компилятор будет вызывать этот оператор.

auto не позволяет использовать неявные операторы преобразования; он принимает тип as-is (конечно, удаление ссылок). auto* также не делает неявных преобразований. Так почему бы ему вызвать неявное преобразование для закрытия лямбда, а не для определенного пользователем типа?

Ответ 3

Лямбда-код не работает по той же причине, что это не работает:

struct foo {
  operator int*() const {
    static int x;
    return &x;
  }
};

int* pint = foo{};
auto* pint2 = foo{}; // does not compile

или даже:

template<class T>
void test(T*) {};
test(foo{});

В лямбда есть оператор, который неявно преобразует его в указатель функции (конкретной), как и foo.

auto не выполняет преобразование. Когда-либо. Auto ведет себя как параметр class T для функции шаблона, где его тип выводится.

Поскольку тип с правой стороны не является указателем, его нельзя использовать для инициализации переменной auto*.

Lambdas не являются указателями на функции. Lambdas не std::function s. Это автозаписываемые функциональные объекты (объекты с operator()).

Изучите это:

void (*ptr)(int) = [](auto x){std::cout << x;};
ptr(7);

он компилируется и работает в gcc (не уверен, что это расширение, теперь, когда я думаю об этом). Однако, что бы сделать auto* ptr = [](auto x){std::cout << x;}?

Однако унарный + - это оператор, который работает с указателями (и почти ничего не делает для них), но не в foo или лямбда.

Итак,

auto* pauto=+foo{};

и

auto* pfun=+[](int x){};

Оба работают, магически.