c++ - 声明一个可以将lambda用作参数的函数,但该lambda必须能够捕获其他变量

标签 c++ c++17

我想制作一个可以将函数作为参数的函数,以便可以在客户端代码中使用指定的lambda调用该函数。目前,我的代码是这样的:

void Execute(float threshold,
               void (*behaviour)(std::shared_ptr<cv::Mat>& image_i,
                       pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_i,
                       const int* position, int index)) 
但是,这将无法编译。
使用:
  template <typename func>
  void Execute(float threshold, func behaviour)
可以编译并且可以正常工作,但是我想限制客户端代码在编译时遵守函数签名。我正在使用C++ 17。
客户端代码示例:
  caster.Execute(thresh,
                 [&](std::shared_ptr<cv::Mat>& image,
                     pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud,
                     const int* position, int index) -> void {
                   pcl::PointXYZ origin(0, 0, 0);
                   float d = beam::distance(cloud->points[index], origin);
                   if (d > max_depth_) { max_depth_ = d; }
                   if (d < min_depth_) { min_depth_  = d; }
                   image->at<float>(position[0], position[1]) = d;
                   num_extracted++;
                 });
如您所见,我想访问在lambda范围之外声明的变量num_extracted,min_depth_和max_depth_。

最佳答案

您要满足两个要求:

  • 将任何lambda约束到给定的签名。
  • 从给定的lambda中访问特定的变量。

  • 这是我可以从您的代码推论得出的有效版本:
    namespace cv {
        struct Mat {
            float  m_mat [3][3] {};
            template<typename TYPE_T>
            float&  at( int ROW, int COL ) { return m_mat[ROW][COL]; }
        };
    }
    
    namespace pcl {
        struct  PointXYZ { int X{}, Y{}, Z{}; };
    
        template<typename TYPE_T>
        struct  PointCloud {
            using Ptr = std::shared_ptr<PointCloud<TYPE_T>>;
            TYPE_T *points{};
            int     length{};
        };
    }
    
    struct beam {
        static float  distance( const pcl::PointXYZ &PT, const pcl::PointXYZ &ORIGIN ) { return 0.0f; }
    };
    
    std::shared_ptr<cv::Mat>             g_image       { new cv::Mat{} };
    pcl::PointCloud<pcl::PointXYZ>::Ptr  g_cloud       { new pcl::PointCloud<pcl::PointXYZ>{} };
    int                                  g_position [] { 0, 0 };
    
    template<typename func>
    requires std::is_invocable_v<func,
        std::shared_ptr<cv::Mat>&, 
        pcl::PointCloud<pcl::PointXYZ>::Ptr&,
        const int*,
        int
    >
    void  Execute( float threshold, func behaviour )
    {
        behaviour(g_image, g_cloud, g_position, 1);
    }
    
    int main()
    {
        int    num_extracted {};
        float  min_depth_    {},
               max_depth_    {};
    
        Execute(1.0f, [&] ( auto &&IMAGE, auto &&CLOUD, auto &&POSITION, auto &&INDEX )
        {
            pcl::PointXYZ origin { 0, 0, 0 };
            float d = beam::distance(CLOUD->points[INDEX], origin);
            if( d > max_depth_ ) { max_depth_ = d; }
            if( d < min_depth_ ) { min_depth_ = d; }
            IMAGE->at<float>(POSITION[0], POSITION[1]) = d;
            num_extracted++;
        });
    
        return 0;
    }
    
  • 有几种方法可以约束lambda签名,但是它们都涉及使用std::is_invovable(或将行为设置为std::function,正如其他人所建议的那样)。我选择使用较新的require语法。
    另请:C++ Constraints and Concepts
  • 您需要两组变量,但是您没有描述它们的来源:i)传递给Execute的变量,以及ii)将在特定lambda中使用的变量,这些变量将传递给Execute。

  • 2.i)这些是g_ *变量。它们必须在调用Execute的范围内可见,以便可以传递给可调用的行为。在这种情况下,Execute是全局函数,因此变量也必须是全局的。
    2.ii)这些是main中的变量。它们必须在创建lambda的范围内可见。在这种情况下,它们必须位于main或global中。
    [编辑]
    对于C++ 17,您可以从以下位置更改执行:
    template<typename func> requires std::is_invocable_v<...>
    
    template<typename func, typename = std::include_if_t<std::is_invocable_v<...>>>
    

    关于c++ - 声明一个可以将lambda用作参数的函数,但该lambda必须能够捕获其他变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64722960/

    相关文章:

    python - 为什么在 C++ 中从标准输入读取行比 Python 慢得多?

    c++ - 继承类模板的参数化构造函数

    c++ - 什么是完全限定名称?

    c++ - `std::optional` 比 `std::shared_ptr` 和 `std::unique_ptr` 有什么优势?

    c++ - 限制可变参数模板参数

    c++ - c++ 中 strcpy 的替代方案

    c++ - 对于隐式定义的构造函数/析构函数,我们如何通过调试器检查值?

    c++ - vcpkg和cmake安装后如何生成c++ grpc文件?

    c++ - 如何通过转发或移动捕获参数包?

    c++ - 位域结构列表