是否可以将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&&'
这是一个要消化的错误消息,但我认为我从中得到的是不能作为constexpr处理,因此我不能将它作为函数指针传递?我也试着让x成为constexpr,但这似乎没有帮助。
正如其他人提到的,你可以用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);
这是另一种解决方案。c++ 14(可以转换为c++ 11)支持返回值、不可复制和可变lambda。如果不需要可变的lambdas,可以通过删除匹配非const版本的专门化并嵌入impl_impl来更短。
对于那些想知道的人来说,它是有效的,因为每个lambda都是唯一的(是不同的类),因此调用to_f会为这个lambda静态和相应的c风格函数生成唯一的,可以访问它。
template <class L, class R, class... Args> static auto impl_impl(L l) {
static_assert(!std::is_same<L, std::function<R(Args...)>>::value,
"Only lambdas are supported, it is unsafe to use "
"std::function or other non-lambda callables");
static L lambda_s = std::move(l);
return +[](Args... args) -> R { return lambda_s(args...); };
}
template <class L>
struct to_f_impl : public to_f_impl<decltype(&L::operator())> {};
template <class ClassType, class R, class... Args>
struct to_f_impl<R (ClassType::*)(Args...) const> {
template <class L> static auto impl(L l) {
return impl_impl<L, R, Args...>(std::move(l));
}
};
template <class ClassType, class R, class... Args>
struct to_f_impl<R (ClassType::*)(Args...)> {
template <class L> static auto impl(L l) {
return impl_impl<L, R, Args...>(std::move(l));
}
};
template <class L> auto to_f(L l) { return to_f_impl<L>::impl(std::move(l)); }
注意,这也适用于其他可调用的对象,如std::function,但如果它不起作用会更好,因为与lambdas不同,std::function这样的对象不会生成唯一类型,因此内部模板和它的内部静态将被所有具有相同签名的函数重用/共享,这很可能不是我们想从它那里得到的。我已经明确禁止std::函数,但还有更多我不知道如何以通用的方式禁止。
这不是一个直接的答案,而是使用“functor”模板模式来隐藏lambda类型的细节,并保持代码的美观和简单。
我不确定你想如何使用决定类,所以我必须用一个使用它的函数扩展类。完整示例请参见: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
有关完整示例,请参见链接
一个类似的答案,但我这样做是为了让你不必指定返回指针的类型(注意,通用版本需要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
}
正如其他人提到的,你可以用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);