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

73
1

Вопрос

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

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

Задний план

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

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

Я стремлюсь к апатриду, потому что, похоже, у 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, а не определении самой лямбда (так, чтобы оно могло быть передано как аргумент шаблона или что-то еще).

спросил(а) 2018-03-10T08:43:00+03:00 2 года, 12 месяцев назад
1
Решение
85

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

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() члена лямбда.

ответил(а) 2018-03-10T09:13:00+03:00 2 года, 12 месяцев назад
44

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

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

ответил(а) 2018-03-10T09:08:00+03:00 2 года, 12 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема