C++:将 lambda 指针作为函数指针传递
C++: Passing lambda pointer as a function pointer
我想获取 C++ 中隐式函数的值。为简单起见,假设我对函数 x(a) 感兴趣,它由 x*x - a == 0.
定义
我有一个使用布伦特方法的寻根器,声明为
BrentsFindRoot( double (*f)(double), double a, double b, double tol )
由于第一个参数是指向函数 double -> double 的指针,我想我可以通过使用 lambda 函数来实现它。所以我定义了
auto Foo(double a) {
auto f = [a] (double x) noexcept -> double {
return x*x - a;
};
return f;
}
我想我可以通过调用 BrentsFindRoot(&Foo(2),0,10,1e-10)
得到 sqrt(2)
。但是,编译器抱怨它无法将 lambda 函数转换为函数指针。我读到只有当 lambda 函数不捕获任何东西时才能做到这一点,但在我的例子中它需要捕获 a
.
那么这里的解决方案是什么?有什么方法可以将 Foo(2)
传递给 BrentsFindRoot
吗?或者我如何重新声明 BrentsFindRoot
以便它接受 lambda 函数?
我正在使用 C++17
您的 BrentsFindRoot
采用无状态函数指针。
你的 lambda 有状态。
这些不兼容。在概念上和句法上。
BrentsFindRoot( double (*f)(void const*, double), void const*, double a, double b, double tol )
如果您添加状态并希望它保持纯 C 函数,这就是签名将如何更改。然后传递一个 lambda 在概念上是可行的,但语法很笨拙。如果您不介意根查找器中的 C++:
BrentsFindRoot( std::function<double(double)> f, double a, double b, double tol )
或者,您可以通过 table/global 状态技巧将状态 shoe-horn 转换为无状态函数指针。您还可以通过获取和存储等同于 a
的内容作为编译时参数来使 lambda 无状态。
但只做 std::function
版本。
如果 BrentsFindRoot
是 header 唯一的功能,您可以使用模板
template<class F>
void BrentsFindRoot( F f, double, double, double );
最后一个选项是查找或编写 function_view
类型;通过避免存储,这比 std::function
更有效。
union function_state {
void* pvoid;
void(* pfvoid)();
function_state(void* p=nullptr):pvoid(p) {}
template<class R, class...Args>
function_state(R(*pf)(Args...)):pfvoid(reinterpret_cast<void(*)()>(pf)) {}
};
template<class Sig>
struct function_view;
template<class R, class...Args>
struct function_view<R(Args...)> {
function_state state;
R(*pf)(function_state, Args&&...args) = nullptr;
R operator()(Args...args)const {
return pf(state, std::forward<Args>(args)...);
}
function_view(function_view const&)=default;
function_view& operator=(function_view const&)=default;
explicit operator bool() const{ return pf; }
function_view( R(*f)(Args...) ):
state(f),
pf([](function_state s, Args&&...args)->R{
return reinterpret_cast<R(*)(Args...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}
template<class F, std::convertible_to<R> FR=std::invoke_result_t< F, Args... >>
requires (!std::is_same_v<R,void>)
function_view( F&& f ):
state((void*)std::addressof(f)),
pf([](function_state s, Args&&...args)->R{
return (*static_cast<F*>(s.pvoid))( std::forward<Args>(args)... );
})
{}
template<class F>
requires (std::is_same_v<R, void>)
function_view( F&& f ):
state((void*)std::addressof(f)),
pf([](function_state s, Args&&...args)->void{
(*static_cast<F*>(s.pvoid))( std::forward<Args>(args)... );
})
{}
template<std::convertible_to<R> R0, std::constructible_from<Args>...As>
requires (!std::is_same_v<R,void>)
function_view( R0(*f)(As...) ):
state(f),
pf([](function_state s, Args&&...args)->R{
return reinterpret_cast<R0(*)(As...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}
template<class R0, std::constructible_from<Args>...As>
requires (std::is_same_v<R, void>)
function_view( R0(*f)(As...) ):
state(f),
pf([](function_state s, Args&&...args)->void{
reinterpret_cast<R0(*)(As...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}
};
但这可能不是你想要写的东西。
除了函数指针或 std::function
,您也可以使用模板:
template<class Callback>
void BrentsFindRoot(Callback const& f, double a, double b, double tol ) {
// ...
}
我想获取 C++ 中隐式函数的值。为简单起见,假设我对函数 x(a) 感兴趣,它由 x*x - a == 0.
定义我有一个使用布伦特方法的寻根器,声明为
BrentsFindRoot( double (*f)(double), double a, double b, double tol )
由于第一个参数是指向函数 double -> double 的指针,我想我可以通过使用 lambda 函数来实现它。所以我定义了
auto Foo(double a) {
auto f = [a] (double x) noexcept -> double {
return x*x - a;
};
return f;
}
我想我可以通过调用 BrentsFindRoot(&Foo(2),0,10,1e-10)
得到 sqrt(2)
。但是,编译器抱怨它无法将 lambda 函数转换为函数指针。我读到只有当 lambda 函数不捕获任何东西时才能做到这一点,但在我的例子中它需要捕获 a
.
那么这里的解决方案是什么?有什么方法可以将 Foo(2)
传递给 BrentsFindRoot
吗?或者我如何重新声明 BrentsFindRoot
以便它接受 lambda 函数?
我正在使用 C++17
您的 BrentsFindRoot
采用无状态函数指针。
你的 lambda 有状态。
这些不兼容。在概念上和句法上。
BrentsFindRoot( double (*f)(void const*, double), void const*, double a, double b, double tol )
如果您添加状态并希望它保持纯 C 函数,这就是签名将如何更改。然后传递一个 lambda 在概念上是可行的,但语法很笨拙。如果您不介意根查找器中的 C++:
BrentsFindRoot( std::function<double(double)> f, double a, double b, double tol )
或者,您可以通过 table/global 状态技巧将状态 shoe-horn 转换为无状态函数指针。您还可以通过获取和存储等同于 a
的内容作为编译时参数来使 lambda 无状态。
但只做 std::function
版本。
如果 BrentsFindRoot
是 header 唯一的功能,您可以使用模板
template<class F>
void BrentsFindRoot( F f, double, double, double );
最后一个选项是查找或编写 function_view
类型;通过避免存储,这比 std::function
更有效。
union function_state {
void* pvoid;
void(* pfvoid)();
function_state(void* p=nullptr):pvoid(p) {}
template<class R, class...Args>
function_state(R(*pf)(Args...)):pfvoid(reinterpret_cast<void(*)()>(pf)) {}
};
template<class Sig>
struct function_view;
template<class R, class...Args>
struct function_view<R(Args...)> {
function_state state;
R(*pf)(function_state, Args&&...args) = nullptr;
R operator()(Args...args)const {
return pf(state, std::forward<Args>(args)...);
}
function_view(function_view const&)=default;
function_view& operator=(function_view const&)=default;
explicit operator bool() const{ return pf; }
function_view( R(*f)(Args...) ):
state(f),
pf([](function_state s, Args&&...args)->R{
return reinterpret_cast<R(*)(Args...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}
template<class F, std::convertible_to<R> FR=std::invoke_result_t< F, Args... >>
requires (!std::is_same_v<R,void>)
function_view( F&& f ):
state((void*)std::addressof(f)),
pf([](function_state s, Args&&...args)->R{
return (*static_cast<F*>(s.pvoid))( std::forward<Args>(args)... );
})
{}
template<class F>
requires (std::is_same_v<R, void>)
function_view( F&& f ):
state((void*)std::addressof(f)),
pf([](function_state s, Args&&...args)->void{
(*static_cast<F*>(s.pvoid))( std::forward<Args>(args)... );
})
{}
template<std::convertible_to<R> R0, std::constructible_from<Args>...As>
requires (!std::is_same_v<R,void>)
function_view( R0(*f)(As...) ):
state(f),
pf([](function_state s, Args&&...args)->R{
return reinterpret_cast<R0(*)(As...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}
template<class R0, std::constructible_from<Args>...As>
requires (std::is_same_v<R, void>)
function_view( R0(*f)(As...) ):
state(f),
pf([](function_state s, Args&&...args)->void{
reinterpret_cast<R0(*)(As...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}
};
但这可能不是你想要写的东西。
除了函数指针或 std::function
,您也可以使用模板:
template<class Callback>
void BrentsFindRoot(Callback const& f, double a, double b, double tol ) {
// ...
}