Получение указателя функции на лямбда?
Я хочу иметь возможность получить указатель на функцию лямбда в С++.
Я могу сделать:
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.
Ответы
Ответ 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){};
Оба работают, магически.