C++ 将捕获 lambda 作为函数指针传递
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28746744/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Passing capturing lambda as function pointer
提问by Cory Kramer
Is it possible to pass a lambda function as a function pointer? If so, I must be doing something incorrectly because I am getting a compile error.
是否可以将 lambda 函数作为函数指针传递?如果是这样,我一定是做错了什么,因为我收到了编译错误。
Consider the following example
考虑下面的例子
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
When I try to compile this, I get the following compilation error:
当我尝试编译它时,出现以下编译错误:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
That's one heck of an error message to digest, but I think what I'm getting out of it is that the lambda cannot be treated as a constexpr
so therefore I cannot pass it as a function pointer? I've tried making x
const as well, but that doesn't seem to help.
这是一个需要消化的错误消息,但我认为我从中得到的是不能将 lambda 视为 aconstexpr
所以我不能将它作为函数指针传递?我也试过制作x
const ,但这似乎没有帮助。
采纳答案by Shafik Yaghmour
A lambda can only be converted to a function pointer if it does not capture, from the draft C++11 standardsection 5.1.2
[expr.prim.lambda]says (emphasis mine):
从C++11 标准部分草案5.1.2
[expr.prim.lambda]说(强调我的),如果 lambda 没有捕获,它只能转换为函数指针:
The closure type for a lambda-expression with no lambda-capturehas a public non-virtual non-explicit const conversion function to pointer to functionhaving the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type's function call operator.
没有 lambda 捕获的 lambda 表达式的闭包类型具有公共非虚拟非显式 const转换函数,指向与闭包类型的函数调用运算符具有相同参数和返回类型的函数的指针。此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果。
Note, cppreference also covers this in their section on Lambda functions.
请注意,cppreference 在其有关Lambda 函数的部分中也涵盖了这一点。
So the following alternatives would work:
因此,以下替代方案将起作用:
typedef bool(*DecisionFn)(int);
Decide greaterThanThree{ []( int x ){ return x > 3; } };
and so would this:
这也是:
typedef bool(*DecisionFn)();
Decide greaterThanThree{ [](){ return true ; } };
and as 5gon12ederpoints out, you can also use std::function
, but note that std::function
is heavy weight, so it is not a cost-less trade-off.
并且正如5gon12eder指出的那样,您也可以使用std::function
,但请注意,std::function
重量很重,因此这不是一种无成本的权衡。
回答by 5gon12eder
Shafik Yaghmour's answercorrectly explains why the lambda cannot be passed as a function pointer if it has a capture. I'd like to show two simple fixes for the problem.
Shafik Yaghmour 的回答正确地解释了为什么 lambda 不能作为函数指针传递,如果它有一个捕获。我想展示两个针对该问题的简单修复。
Use
std::function
instead of raw function pointers.This is a very clean solution. Note however that it includes some additional overhead for the type erasure (probably a virtual function call).
#include <functional> #include <utility> struct Decide { using DecisionFn = std::function<bool()>; Decide(DecisionFn dec) : dec_ {std::move(dec)} {} DecisionFn dec_; }; int main() { int x = 5; Decide greaterThanThree { [x](){ return x > 3; } }; }
Use a lambda expression that doesn't capture anything.
Since your predicate is really just a boolean constant, the following would quickly work around the current issue. See this answerfor a good explanation why and how this is working.
// Your 'Decide' class as in your post. int main() { int x = 5; Decide greaterThanThree { (x > 3) ? [](){ return true; } : [](){ return false; } }; }
使用
std::function
代替原始函数指针。这是一个非常干净的解决方案。但是请注意,它包括一些额外的类型擦除开销(可能是虚拟函数调用)。
#include <functional> #include <utility> struct Decide { using DecisionFn = std::function<bool()>; Decide(DecisionFn dec) : dec_ {std::move(dec)} {} DecisionFn dec_; }; int main() { int x = 5; Decide greaterThanThree { [x](){ return x > 3; } }; }
使用不捕获任何内容的 lambda 表达式。
由于您的谓词实际上只是一个布尔常量,因此以下内容将快速解决当前问题。请参阅此答案以了解其工作原理和方式。
// Your 'Decide' class as in your post. int main() { int x = 5; Decide greaterThanThree { (x > 3) ? [](){ return true; } : [](){ return false; } }; }
回答by Noxxer
Lambda expressions, even captured ones, can be handled as a function pointer (pointer to member function).
Lambda 表达式,即使是捕获的表达式,也可以作为函数指针(指向成员函数的指针)来处理。
It is tricky because an lambda expression is not a simple function. It is actually an object with an operator().
这很棘手,因为 lambda 表达式不是一个简单的函数。它实际上是一个带有 operator() 的对象。
When you are creative, you can use this! Think of an "function" class in style of std::function. If you save the object you also can use the function pointer.
当你有创意时,你可以使用它!想想 std::function 风格的“函数”类。如果您保存对象,您还可以使用函数指针。
To use the function pointer, you can use the following:
要使用函数指针,您可以使用以下命令:
int first = 5;
auto lambda = [=](int x, int z) {
return x + z + first;
};
int(decltype(lambda)::*ptr)(int, int)const = &decltype(lambda)::operator();
std::cout << "test = " << (lambda.*ptr)(2, 3) << std::endl;
To build a class that can start working like a "std::function", first you need a class/struct than can store object and function pointer. Also you need an operator() to execute it:
要构建一个可以像“std::function”一样开始工作的类,首先你需要一个可以存储对象和函数指针的类/结构。您还需要一个 operator() 来执行它:
// OT => Object Type
// RT => Return Type
// A ... => Arguments
template<typename OT, typename RT, typename ... A>
struct lambda_expression {
OT _object;
RT(OT::*_function)(A...)const;
lambda_expression(const OT & object)
: _object(object), _function(&decltype(_object)::operator()) {}
RT operator() (A ... args) const {
return (_object.*_function)(args...);
}
};
With this you can now run captured, non-captured lambdas, just like you are using the original:
有了这个,您现在可以运行捕获的、非捕获的 lambda,就像您使用原始的一样:
auto capture_lambda() {
int first = 5;
auto lambda = [=](int x, int z) {
return x + z + first;
};
return lambda_expression<decltype(lambda), int, int, int>(lambda);
}
auto noncapture_lambda() {
auto lambda = [](int x, int z) {
return x + z;
};
return lambda_expression<decltype(lambda), int, int, int>(lambda);
}
void refcapture_lambda() {
int test;
auto lambda = [&](int x, int z) {
test = x + z;
};
lambda_expression<decltype(lambda), void, int, int>f(lambda);
f(2, 3);
std::cout << "test value = " << test << std::endl;
}
int main(int argc, char **argv) {
auto f_capture = capture_lambda();
auto f_noncapture = noncapture_lambda();
std::cout << "main test = " << f_capture(2, 3) << std::endl;
std::cout << "main test = " << f_noncapture(2, 3) << std::endl;
refcapture_lambda();
system("PAUSE");
return 0;
}
This code works with VS2015
此代码适用于 VS2015
Update 04.07.17:
17 年 7 月 4 日更新:
template <typename CT, typename ... A> struct function
: public function<decltype(&CT::operator())(A...)> {};
template <typename C> struct function<C> {
private:
C mObject;
public:
function(const C & obj)
: mObject(obj) {}
template<typename... Args> typename
std::result_of<C(Args...)>::type operator()(Args... a) {
return this->mObject.operator()(a...);
}
template<typename... Args> typename
std::result_of<const C(Args...)>::type operator()(Args... a) const {
return this->mObject.operator()(a...);
}
};
namespace make {
template<typename C> auto function(const C & obj) {
return ::function<C>(obj);
}
}
int main(int argc, char ** argv) {
auto func = make::function([](int y, int x) { return x*y; });
std::cout << func(2, 4) << std::endl;
system("PAUSE");
return 0;
}
回答by Passer By
Capturing lambdas cannot be converted to function pointers, as this answerpointed out.
正如this answer指出的那样,捕获lambdas不能转换为函数指针。
However, it is often quite a pain to supply a function pointer to an API that only accepts one. The most often cited method to do so is to provide a function and call a static object with it.
然而,为一个只接受一个的 API 提供一个函数指针通常是一件很痛苦的事情。最常被引用的方法是提供一个函数并用它调用一个静态对象。
static Callable callable;
static bool wrapper()
{
return callable();
}
This is tedious. We take this idea further and automate the process of creating wrapper
and make life much easier.
这很乏味。我们将这个想法更进一步,使创造过程自动化wrapper
,让生活变得更轻松。
#include<type_traits>
#include<utility>
template<typename Callable>
union storage
{
storage() {}
std::decay_t<Callable> callable;
};
template<int, typename Callable, typename Ret, typename... Args>
auto fnptr_(Callable&& c, Ret (*)(Args...))
{
static bool used = false;
static storage<Callable> s;
using type = decltype(s.callable);
if(used)
s.callable.~type();
new (&s.callable) type(std::forward<Callable>(c));
used = true;
return [](Args... args) -> Ret {
return Ret(s.callable(std::forward<Args>(args)...));
};
}
template<typename Fn, int N = 0, typename Callable>
Fn* fnptr(Callable&& c)
{
return fnptr_<N>(std::forward<Callable>(c), (Fn*)nullptr);
}
And use it as
并将其用作
void foo(void (*fn)())
{
fn();
}
int main()
{
int i = 42;
auto fn = fnptr<void()>([i]{std::cout << i;});
foo(fn); // compiles!
}
This is essentially declaring an anonymous function at each occurrence of fnptr
.
这实质上是在每次出现 时声明一个匿名函数fnptr
。
Note that invocations of fnptr
overwrite the previously written callable
given callables of the same type. We remedy this, to a certain degree, with the int
parameter N
.
请注意,调用fnptr
覆盖先前编写callable
的相同类型的给定可调用对象。我们在一定程度上使用int
参数来解决这个问题N
。
std::function<void()> func1, func2;
auto fn1 = fnptr<void(), 1>(func1);
auto fn2 = fnptr<void(), 2>(func2); // different function
回答by smallscript
While the template approach is clever for various reasons, it is important to remember the lifecycle of the lambda and the captured variables. If any form of a lambda pointer is is going to be used and the lambda is not a downward continuation, then only a copying [=] lambda should used. I.e., even then, capturing a pointer to a variable on the stack is UNSAFE if the lifetime of those captured pointers (stack unwind) is shorter than the lifetime of the lambda.
虽然模板方法出于各种原因很聪明,但重要的是要记住 lambda 的生命周期和捕获的变量。如果要使用任何形式的 lambda 指针,并且 lambda 不是向下延续,则只应使用复制 [=] lambda。即,即使那样,如果这些捕获的指针的生命周期(堆栈展开)比 lambda 的生命周期短,那么捕获指向堆栈上变量的指针也是不安全的。
A simpler solution for capturing a lambda as a pointer is:
将 lambda 捕获为指针的更简单的解决方案是:
auto pLamdba = new std::function<...fn-sig...>([=](...fn-sig...){...});
auto pLamdba = new std::function<...fn-sig...>([=](...fn-sig...){...});
e.g., new std::function<void()>([=]() -> void {...}
例如, new std::function<void()>([=]() -> void {...}
Just remember to later delete pLamdba
so ensure that you don't leak the lambda memory.
Secret to realize here is that lambdas can capture lambdas (ask yourself how that works) and also that in order for std::function
to work generically the lambda implementation needs to contain sufficient internal information to provide access to the size of the lambda (and captured) data (which is why the delete
should work [running destructors of captured types]).
请记住稍后,delete pLamdba
以确保您不会泄漏 lambda 内存。这里要意识到的秘密是 lambdas 可以捕获 lambdas(问问自己它是如何工作的),而且为了std::function
通用地工作,lambda 实现需要包含足够的内部信息以提供对 lambda(和捕获的)数据大小的访问(这就是为什么delete
应该工作 [运行捕获类型的析构函数])。
回答by janCoffee
A shortcut for using a lambda with as a C function pointer is this:
将 lambda 用作 C 函数指针的快捷方式是:
"auto fun = +[](){}"
Using Curl as exmample (curl debug info)
使用 Curl 作为示例(curl 调试信息)
auto callback = +[](CURL* handle, curl_infotype type, char* data, size_t size, void*){ //add code here :-) };
curl_easy_setopt(curlHande, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curlHande,CURLOPT_DEBUGFUNCTION,callback);
回答by code_fodder
Not a direct answer, but a slight variation to use the "functor" template pattern to hide away the specifics of the lambda type and keeps the code nice and simple.
不是直接的答案,而是使用“函子”模板模式来隐藏 lambda 类型的细节并保持代码简洁明了的细微变化。
I was not sure how you wanted to use the decide class so I had to extend the class with a function that uses it. See full example here: https://godbolt.org/z/jtByqE
我不确定你想如何使用决定类,所以我不得不用一个使用它的函数来扩展这个类。在此处查看完整示例:https: //godbolt.org/z/jtByqE
The basic form of your class might look like this:
类的基本形式可能如下所示:
template <typename Functor>
class Decide
{
public:
Decide(Functor dec) : _dec{dec} {}
private:
Functor _dec;
};
Where you pass the type of the function in as part of the class type used like:
您将函数的类型作为类类型的一部分传入,例如:
auto decide_fc = [](int x){ return x > 3; };
Decide<decltype(decide_fc)> greaterThanThree{decide_fc};
Again, I was not sure why you are capturing x
it made more sense (to me) to have a parameter that you pass in to the lambda) so you can use like:
同样,我不确定为什么你要捕获x
它(对我来说)有一个你传递给 lambda 的参数更有意义)所以你可以使用:
int result = _dec(5); // or whatever value
See the link for a complete example
查看完整示例的链接
回答by beniekg
As it was mentioned by the others you can substitute Lambda function instead of function pointer. I am using this method in my C++ interface to F77 ODE solver RKSUITE.
正如其他人提到的,您可以替换 Lambda 函数而不是函数指针。我在 F77 ODE 求解器 RKSUITE 的 C++ 接口中使用此方法。
//C interface to Fortran subroutine UT
extern "C" void UT(void(*)(double*,double*,double*),double*,double*,double*,
double*,double*,double*,int*);
// C++ wrapper which calls extern "C" void UT routine
static void rk_ut(void(*)(double*,double*,double*),double*,double*,double*,
double*,double*,double*,int*);
// Call of rk_ut with lambda passed instead of function pointer to derivative
// routine
mathlib::RungeKuttaSolver::rk_ut([](double* T,double* Y,double* YP)->void{YP[0]=Y[1]; YP[1]= -Y[0];}, TWANT,T,Y,YP,YMAX,WORK,UFLAG);