传递lambda作为函数指针
是否有可能传递一个lambda函数作为函数指针? 如果是这样,我必须做错误的东西,因为我得到一个编译错误。
考虑下面的例子
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; }
当我尝试编译这个 ,我得到以下编译错误:
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&&'
这是一个错误消息的摘要,但我想我得到的是,lambda不能被视为一个constexpr
所以我不能把它作为一个函数指针吗? 我已经尝试使x
const为好,但是这似乎没有帮助。
一个lambda只能转换为一个函数指针,如果它没有捕获,从草案C ++ 11标准部分5.1.2
[expr.prim.lambda]说( 强调我的 ):
没有lambda捕获的lambda表达式的闭包类型具有公共的非虚拟非显式const 转换函数,指向具有与闭包类型的函数调用运算符相同的参数和返回类型的函数。 这个转换函数返回的值应该是被调用的函数的地址,和调用闭包类型的函数调用操作符一样。
请注意,cppreference也在关于Lambda函数的章节中介绍了这一点。
所以下面的选择是可行的:
typedef bool(*DecisionFn)(int); Decide greaterThanThree{ []( int x ){ return x > 3; } };
这样做也是如此:
typedef bool(*DecisionFn)(); Decide greaterThanThree{ [](){ return true ; } };
正如5gon12eder指出的那样,你也可以使用std :: function ,但是请注意std :: function是重量级的 ,所以它不是一个低成本的折衷。
Shafik Yaghmour的答案正确地解释了为什么lambda不能作为函数指针传递。 我想为这个问题展示两个简单的修复。
-
使用
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; } }; }
我知道这有点旧
但是我想补充一下:
Lambda表达式(甚至是捕获的)可以作为一个函数指针来处理!
这是棘手的,因为Lambda表达式不是一个简单的函数。 它实际上是一个运算符()的对象。
当你有创意时,你可以使用这个! 想象一下std :: 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;
要构建一个可以像“std :: function”一样开始工作的类,我只是做一个简短的例子。 首先,你需要一个类/结构比可以存储对象和函数指针还需要一个运算符()来执行它:
// 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...); } };
有了这个,您现在可以运行捕获的未捕获的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; }
这个代码适用于VS2015希望它有助于:)
映入眼帘!
编辑:删除针模板FP,删除函数指针参数,重命名为lambda_expression
更新04.07.17:
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; }
正如其他人提到的,你可以用Lambda函数来代替函数指针。 我在我的C ++接口中使用这个方法来F77 ODE求解器RKSUITE。
//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不能被转换为函数指针。
然而,提供一个函数指针给一个只接受一个API的API通常是很痛苦的。 最经常引用的方法是提供一个函数并用它调用一个静态对象。
static Callable callable; static bool wrapper() { return callable(); }
这很乏味。 我们进一步采取这个想法,并自动化创建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(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! }
生活
这本质上是在fnptr
每一次出现处声明一个匿名函数。
请注意, fnptr
调用fnptr
覆盖先前写入的相同类型的callable
给定可调用对象。 我们在一定程度上用int
参数N
来补救这个问题。
std::function<void()> func1, func2; auto fn1 = fnptr<void(), 1>(func1); auto fn2 = fnptr<void(), 2>(func2); // different function
如果你足够疯狂, N
可以拖延到constexpr柜台 ,以便进一步放松使用。