是否可以将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;
}
当我try to compile this时,我收到以下编译错误:
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,但这似乎没什么帮助。
答案 0 :(得分:171)
如果lambda不能从draft C++11 standard部分5.1.2
[expr.prim.lambda] 表示,则只能将其转换为函数指针。 >强调我的):
没有lambda-capture 的lambda表达式的闭包类型有一个 公共非虚拟非显式const 转换函数到指针 使用具有与闭包相同的参数和返回类型 type的函数调用运算符。此转换返回的值 function应该是一个函数的地址,当被调用时,它具有 与调用闭包类型的函数调用操作符的效果相同。
请注意,cppreference还会在Lambda functions的部分中介绍这一点。
所以以下替代方案可行:
typedef bool(*DecisionFn)(int);
Decide greaterThanThree{ []( int x ){ return x > 3; } };
所以这样:
typedef bool(*DecisionFn)();
Decide greaterThanThree{ [](){ return true ; } };
并且5gon12eder指出,您也可以使用std::function
,但请注意std::function
is heavy weight,因此这不是一种无成本的权衡。
答案 1 :(得分:81)
Shafik Yaghmour's answer正确解释了为什么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表达式。
由于您的谓词实际上只是一个布尔常量,因此以下内容可以快速解决当前问题。请参阅this answer以获得有关其原因和方式的详细解释。
// Your 'Decide' class as in your post.
int
main()
{
int x = 5;
Decide greaterThanThree {
(x > 3) ? [](){ return true; } : [](){ return false; }
};
}
答案 2 :(得分:27)
我知道这有点老..
但我想补充一下:
Lambda表达式(甚至是捕获的表达式)可以作为函数指针处理!
这很棘手,因为Lambda表达式不是一个简单的函数。它实际上是一个带有operator()的对象。
当你有创意时,你可以使用它! 想想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”一样开始工作的类,我将做一个简短的例子。首先,你需要一个类/结构,而不是存储对象和函数指针,你还需要一个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...);
}
};
有了这个,您现在可以运行捕获的非捕获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;
}
答案 3 :(得分:8)
捕获lambda不能转换为函数指针,正如this answer指出的那样。
但是,向只接受一个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(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!
}
这基本上是在每次出现fnptr
时声明一个匿名函数。
请注意,fnptr
的调用会覆盖先前编写的callable
给定的相同类型的callable。我们在一定程度上使用int
参数N
补救了这一点。
std::function<void()> func1, func2;
auto fn1 = fnptr<void(), 1>(func1);
auto fn2 = fnptr<void(), 2>(func2); // different function
答案 4 :(得分:1)
不是直接的答案,但是使用“ functor”模板模式进行了一些改动,以隐藏lambda类型的细节,并使代码简洁美观。
我不确定您想如何使用Decision类,因此我不得不使用一个使用该类的函数来扩展该类。在此处查看完整的示例:https://godbolt.org/z/jtByqE
课程的基本形式可能如下所示:
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};
再次,我不确定为什么要捕获x
(对我来说)将参数传递给lambda更有意义(对我来说),这样就可以使用:
int result = _dec(5); // or whatever value
有关完整示例,请参见链接
答案 5 :(得分:0)
尽管出于各种原因,模板方法很聪明,但是记住lambda和捕获的变量的生命周期很重要。如果将使用任何形式的lambda指针,并且lambda不是向下的延续,则仅应使用复制[=] lambda。即,即使那样,如果捕获的指针的生存期(堆栈展开)比lambda的生存期短,那么捕获到堆栈上变量的指针也是不安全的。
一种用于捕获lambda作为指针的更简单解决方案是:
auto pLamdba = new std::function<...fn-sig...>([=](...fn-sig...){...});
例如new std::function<void()>([=]() -> void {...}
请记住稍后再delete pLamdba
,以确保您不会泄漏lambda内存。
在这里实现的秘密是,lambda可以捕获lambda(询问自己的工作方式),并且为了使std::function
能够正常工作,lambda实现需要包含足够的内部信息以提供对lambda大小的访问(和捕获的数据)(这就是delete
应该工作的原因[运行捕获类型的析构函数])。
答案 6 :(得分:-1)
正如其他人所提到的,你可以用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);
答案 7 :(得分:-1)
使用lambda作为C函数指针的快捷方式是:
"auto fun = +[](){}"
使用Curl作为示例(curl debug info)
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);
答案 8 :(得分:-1)
一个模拟答案,但是我做到了,所以您不必指定返回的指针的类型(请注意,通用版本需要C ++ 20):
#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
}