【问题标题】:C++: Passing lambda pointer as a function pointerC++:将 lambda 指针作为函数指针传递
【发布时间】:2021-10-05 01:55:17
【问题描述】:

我想在 C++ 中获取隐式函数的值。为简单起见,假设我对由 x*x - a == 0 定义的函数 x(a) 感兴趣。

我有一个使用 Brent 方法的根查找器,该方法声明为

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

【问题讨论】:

    标签: c++ pointers lambda function-pointers


    【解决方案1】:

    您的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 )
    

    或者,您可以通过表/全局状态技巧将状态转换为无状态函数指针。您还可以通过获取和存储与 a 等效的内容作为编译时参数来使 lambda 无状态。

    但只需使用std::function 版本。

    如果BrentsFindRoot 是一个只有头部的函数,你可以使用一个模板

    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)... );
        })
      {}
    };
    

    但这可能不是你想写的东西。

    【讨论】:

    • 也许还值得一提的是,lambda 不是一个函数,而是一个带有operator() 的对象,并且只考虑将指针传递给 lambda 是错误的路径
    • @4603 如果 C API 是上面的那个,你最终会传递一个指向 lambda 的指针和一个用于 f 的包装 lambda。我对教授 C++ 类型名称和术语不感兴趣,尽管它很有用。
    【解决方案2】:

    除了函数指针或std::function,您也可以使用模板:

    template<class Callback>
    void BrentsFindRoot(Callback const& f, double a, double b, double tol ) {
        // ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-17
      • 2017-10-10
      • 1970-01-01
      • 1970-01-01
      • 2017-11-26
      • 1970-01-01
      • 1970-01-01
      • 2013-04-28
      相关资源
      最近更新 更多