Гнездо без захвата лямбда в C ++ с функциями?

Вопрос

Можно ли вкладывать лямбды без захвата при передаче их в C ++?

Из того, что я могу собрать, кажется, не так; скорее, вам нужно создать класс шаблона, определить статические функции, а затем передать их.

Фон

Связанные с этим постом, для pybind11: Возможно ли, чтобы логические операции (например, `ndarray .__ eq__`) не возвращали` bool` в NumPy?

По сути, я хочу посмотреть, смогу ли я избежать макросов и по-прежнему предоставлять функции без сохранения состояния (без какого-либо стирания типа void* data shenanigans) для использования с Numpy UFunc API.

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

Слепой взлом

Вот мой взлом:

// Goal: Wrap a function of type `Func` without capture.
typedef void (*Func)();// NOPE: `b` ain't stateless.
Func wrap(Func a) {
return [a]() { a(); };
}
int main() {
Func a = []() { cout << "A1\n"; };
wrap(a)();
}// KINDA: Compiles, but `a` wasn't a parameter :(
// - Could use arrays a constexpr indices to fake it :( * 2
int main() {
static const Func a = []() { cout << "A1\n"; };
Func b = []() { a(); };
b();
}

// YUP-ish: Works, cannot deal with lambdas :(
template <Func a>
Func wrap() {
return []() { a(); };
}
// - Cannot be in a class in function scope :(
void a2() { cout << "A2\n"; }
int main() {
Func b = wrap<tmp::a2>();
b();
}

// NOPE: Lambda doesn't have default constructor (but what else would it do???)
int main() {
auto a = []() { cout << "A3\n"; };
using A = decltype(a);
Func b = []() { A a{}; a(); };
b();
}

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

2

Решение

Вы можете скопировать лямбду в статическую переменную:

template<typename F>
auto wrap(F a) {
static auto F b = std::move(a);
return []() { b(); };
}

Каждая лямбда имеет свой тип, поэтому для каждой лямбды создается новая статическая переменная, поскольку функция является шаблонной.

Обратите внимание, что это работает, только если переданный вызываемый объект имеет другой тип. Если вы используете только объект функции без сохранения состояния, у вас не возникнет никаких проблем.

В качестве безопасного охранника вы также можете обеспечить отправку только лямбда без захвата:

template<typename F, std::void_t<
decltype(+std::declval<F>()),
decltype(&F::operator())
>* = nullptr>
auto wrap(F a) {
static auto F b = std::move(a);
return []() { b(); };
}

Sfinae выражение ищет одинарное operator+ что с безмозглой лямбда, и это также искать наличие operator() член лямбды.

3

Другие решения

Лямбда-функция — это не указатель на функцию, это объект. Хотя он может быть преобразован в указатель, он не может быть возвращен как указатель, как и любой локальный объект не может быть возвращен через указатель, должна быть возвращена копия. Правильный код ниже.

#include <functional>
using namespace std;

typedef void (*Func)();

std::function<void()> wrap(Func a) {
return [a]() { a(); };
}

Как насчет последнего. Вы объявили «функцию» a Вот A a; но не назначена функция «тело». Попробуй это

int main() {
auto a = []() { cout << "A3\n"; };
using A = decltype(a);
auto b = [a]() { A c(a); c(); };
b();
}
0

По вопросам рекламы [email protected]