在 C++ 中使用函数嵌套无捕获 lambdas?
Nest captureless lambdas in C++ with functions?
问题
在 C++ 中传递无捕获 lambda 时是否可以嵌套它们?
据我所知,情况似乎并非如此;相反,您似乎必须制作一个模板 class,并定义静态函数,然后传递它们。
背景
与此相关post,对于pybind11
:Possible to have logical operations (e.g. `ndarray.__eq__`) not return `bool` in NumPy?
基本上,我想看看我是否可以避免宏并仍然提供无状态函数(没有任何类型擦除 void* data
恶作剧)与 Numpy UFunc API.
我的目标是无状态,因为看起来 API 有一些函数可以有 void* data
,我可以用它来传递另一个函数指针(或通过捕获擦除的 lambda) , 但其中一些似乎没有。
盲目黑客
这是我的黑客攻击:
// 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
lambda 有一些新内容,但这似乎更多是关于 return 值 constexpr
,而不是 lambda 本身的定义(这样它就可以作为模板参数或其他任何方式传递)。
Lambda 函数不是指向函数的指针,它是一个对象。虽然可以转换为指针,但不能作为指针返回,就像任何本地对象都不能通过指针返回一样,应该返回一个副本。正确代码如下。
#include <functional>
using namespace std;
typedef void (*Func)();
std::function<void()> wrap(Func a) {
return [a]() { a(); };
}
最后呢。您在此处 "function" a
声明了 A a;
但未分配函数 "body"。试试这个
int main() {
auto a = []() { cout << "A3\n"; };
using A = decltype(a);
auto b = [a]() { A c(a); c(); };
b();
}
您可以将 lambda 复制到静态变量中:
template<typename F>
auto wrap(F a) {
static auto F b = std::move(a);
return []() { b(); };
}
每个 lambda 都有不同的类型,因此对于每个 lambda,都会创建一个新的静态变量,因为函数是模板化的。
请注意,它仅在传递的可调用对象具有不同类型时才有效。如果你只使用无状态函数对象,你不会有任何问题。
作为安全卫士,您还可以确保仅发送未捕获的 lambda:
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 表达式查找具有 catpureless lambda 的一元 operator+
,它还会查找 lambda 的 operator()
成员的存在。
问题
在 C++ 中传递无捕获 lambda 时是否可以嵌套它们?
据我所知,情况似乎并非如此;相反,您似乎必须制作一个模板 class,并定义静态函数,然后传递它们。
背景
与此相关post,对于pybind11
:Possible to have logical operations (e.g. `ndarray.__eq__`) not return `bool` in NumPy?
基本上,我想看看我是否可以避免宏并仍然提供无状态函数(没有任何类型擦除 void* data
恶作剧)与 Numpy UFunc API.
我的目标是无状态,因为看起来 API 有一些函数可以有 void* data
,我可以用它来传递另一个函数指针(或通过捕获擦除的 lambda) , 但其中一些似乎没有。
盲目黑客
这是我的黑客攻击:
// 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
lambda 有一些新内容,但这似乎更多是关于 return 值 constexpr
,而不是 lambda 本身的定义(这样它就可以作为模板参数或其他任何方式传递)。
Lambda 函数不是指向函数的指针,它是一个对象。虽然可以转换为指针,但不能作为指针返回,就像任何本地对象都不能通过指针返回一样,应该返回一个副本。正确代码如下。
#include <functional>
using namespace std;
typedef void (*Func)();
std::function<void()> wrap(Func a) {
return [a]() { a(); };
}
最后呢。您在此处 "function" a
声明了 A a;
但未分配函数 "body"。试试这个
int main() {
auto a = []() { cout << "A3\n"; };
using A = decltype(a);
auto b = [a]() { A c(a); c(); };
b();
}
您可以将 lambda 复制到静态变量中:
template<typename F>
auto wrap(F a) {
static auto F b = std::move(a);
return []() { b(); };
}
每个 lambda 都有不同的类型,因此对于每个 lambda,都会创建一个新的静态变量,因为函数是模板化的。
请注意,它仅在传递的可调用对象具有不同类型时才有效。如果你只使用无状态函数对象,你不会有任何问题。
作为安全卫士,您还可以确保仅发送未捕获的 lambda:
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 表达式查找具有 catpureless lambda 的一元 operator+
,它还会查找 lambda 的 operator()
成员的存在。