C++ 将lambda作为函数指针传递
是否可以将lambda函数作为函数指针传递?如果是这样的话,我一定是做错了什么,因为我得到了一个编译错误 考虑下面的例子C++ 将lambda作为函数指针传递,c++,c++11,lambda,function-pointers,C++,C++11,Lambda,Function Pointers,是否可以将lambda函数作为函数指针传递?如果是这样的话,我一定是做错了什么,因为我得到了一个编译错误 考虑下面的例子 using DecisionFn = bool(*)(); class Decide { public: Decide(DecisionFn dec) : _dec{dec} {} private: DecisionFn _dec; }; int main() { int x = 5; Decide greaterThanThree{ [x
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;
}
当输入时,我得到以下编译错误:
函数“int main()”中的:
17:31:错误:“x”的值在常量表达式中不可用
16:9:注意:“int x”不是常量
17:53:错误:调用'Decise::Decise()'时没有匹配的函数
17:53:注:候选人为:
注:decision::decision(DecisionFn)
9:5:注意:参数1没有从'main()::'到'DecisionFn{aka bool(*)()()}的已知转换
6:7:注意:constexpr decise::decise(const decise&)
6:7:注意:参数1没有从'main()::'到'const decision&'的已知转换
6:7:注意:constexpr decise::decise(decise&&)
6:7:注意:参数1没有从“main()::”到“decision&&”的已知转换
这是一条需要消化的错误消息,但我想我从中得到的是lambda不能被视为
constexpr
,因此我不能将其作为函数指针传递?我也尝试过使x
常数,但这似乎没有帮助。lambda只能在没有捕获的情况下转换为函数指针,从5.1.2
[expr.prim.lambda]一节可以看出(我的重点是):
没有lambda捕获的lambda表达式的闭包类型具有
公共非虚拟非显式常量到指针的转换函数
具有与闭包相同的参数和返回类型的函数
类型的函数调用运算符。此转换返回的值
函数应为调用时具有
与调用闭包类型的函数调用操作符的效果相同
请注意,CPPFREFERENCE在其关于的章节中也介绍了这一点
因此,以下备选方案可行:
typedef bool(*DecisionFn)(int);
Decide greaterThanThree{ []( int x ){ return x > 3; } };
这也会:
typedef bool(*DecisionFn)();
Decide greaterThanThree{ [](){ return true ; } };
正如所指出的,您也可以使用,但请注意,这不是一个成本较低的折衷。正确地解释了为什么lambda不能作为函数指针传递,如果它有一个捕获。我想展示两个解决这个问题的简单方法
std::function
代替原始函数指针。
这是一个非常干净的解决方案。但是请注意,它包括一些额外的类型擦除开销(可能是一个虚拟函数调用)
正如其他人提到的,可以用Lambda函数代替函数指针。我在C++接口中使用这个方法来解决F77的ODE求解器RKSuff.
//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);
Lambda表达式,甚至捕获的表达式,都可以作为函数指针(指向成员函数的指针)处理 这很棘手,因为lambda表达式不是一个简单的函数。它实际上是一个带有运算符()的对象 当你有创意的时候,你可以用这个! 设想一个std::function样式的“function”类。 如果保存对象,还可以使用函数指针 要使用函数指针,可以使用以下命令:
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;
int first=5;
自动lambda=[=](整数x,整数z){
首先返回x+z+;
};
int(decltype(lambda)::*ptr)(int,int)const=&decltype(lambda)::操作符();
std::cout参数
模板
结构lambda_表达式{
OT_对象;
RT(OT::*_函数)(A…)常数;
lambda_表达式(常量和对象)
:_对象(对象),_函数(&decltype(_对象)::operator()){}
RT运算符()(A…args)常量{
返回(_对象。*_函数)(参数…);
}
};
使用此功能,您现在可以运行捕获的、未捕获的lambdas,就像您使用原始的:
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;
}
auto-capture_lambda(){
int first=5;
自动lambda=[=](整数x,整数z){
首先返回x+z+;
};
返回lambda_表达式(lambda);
}
自动非捕获λ(){
自动lambda=[](整数x,整数z){
返回x+z;
};
返回lambda_表达式(lambda);
}
void refcapture_lambda(){
智力测验;
自动lambda=[&](整数x,整数z){
试验=x+z;
};
lambda_表达式f(lambda);
f(2,3);
正如前面指出的,捕获lambda不能转换为函数指针
然而,提供一个指向只接受一个API的函数指针通常是一件非常痛苦的事情。最常被引用的方法是提供一个函数并用它调用一个静态对象
static Callable callable;
static bool wrapper()
{
return callable();
}
这很乏味。我们将这一想法进一步推广,并将创建包装器的过程自动化,从而使生活更加轻松
#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);
}
由于各种原因,模板方法很聪明,但记住lambda的生命周期和捕获的变量很重要。如果要使用任何形式的lambda指针,并且lambda不是向下的延续,那么只需要复制[=]应使用lambda。即,即使如此,如果捕获的指针(堆栈展开)的生存期短于lambda的生存期,则捕获堆栈上变量的指针也是不安全的
将lambda捕获为指针的更简单解决方案是:
auto pLamdba = new std::function<...fn-sig...>([=](...fn-sig...){...});
autoplamdba=newstd::function([=](…fn sig…{…});
e、 例如,newstd::function([=]()->void{…}
只需记住稍后删除pLamdba
,以确保不会泄漏lambda内存。
这里要了解的秘密是,lambda可以捕获lambda(问问你自己这是如何工作的),而且为了使std::function
能够正常工作,lambda实现需要包含足够的内部信息,以提供对lambda(和捕获的)数据大小的访问(这就是为什么删除应该起作用[运行捕获类型的析构函数]。使用lambda作为C函数指针的快捷方式如下:
"auto fun = +[](){}"
使用Curl作为exmample()
这不是一个直接的答案,而是使用“函子”模板模式来隐藏
static Callable callable;
static bool wrapper()
{
return callable();
}
#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);
}
void foo(void (*fn)())
{
fn();
}
int main()
{
int i = 42;
auto fn = fnptr<void()>([i]{std::cout << i;});
foo(fn); // compiles!
}
std::function<void()> func1, func2;
auto fn1 = fnptr<void(), 1>(func1);
auto fn2 = fnptr<void(), 2>(func2); // different function
auto pLamdba = new std::function<...fn-sig...>([=](...fn-sig...){...});
"auto fun = +[](){}"
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);
template <typename Functor>
class Decide
{
public:
Decide(Functor dec) : _dec{dec} {}
private:
Functor _dec;
};
auto decide_fc = [](int x){ return x > 3; };
Decide<decltype(decide_fc)> greaterThanThree{decide_fc};
int result = _dec(5); // or whatever value
#include <iostream>
template<typename Function>
struct function_traits;
template <typename Ret, typename... Args>
struct function_traits<Ret(Args...)> {
typedef Ret(*ptr)(Args...);
};
template <typename Ret, typename... Args>
struct function_traits<Ret(*const)(Args...)> : function_traits<Ret(Args...)> {};
template <typename Cls, typename Ret, typename... Args>
struct function_traits<Ret(Cls::*)(Args...) const> : function_traits<Ret(Args...)> {};
using voidfun = void(*)();
template <typename F>
voidfun lambda_to_void_function(F lambda) {
static auto lambda_copy = lambda;
return []() {
lambda_copy();
};
}
// requires C++20
template <typename F>
auto lambda_to_pointer(F lambda) -> typename function_traits<decltype(&F::operator())>::ptr {
static auto lambda_copy = lambda;
return []<typename... Args>(Args... args) {
return lambda_copy(args...);
};
}
int main() {
int num;
void(*foo)() = lambda_to_void_function([&num]() {
num = 1234;
});
foo();
std::cout << num << std::endl; // 1234
int(*bar)(int) = lambda_to_pointer([&](int a) -> int {
num = a;
return a;
});
std::cout << bar(4321) << std::endl; // 4321
std::cout << num << std::endl; // 4321
}