【问题标题】:Captured variables in lambda functions confusion在 lambda 函数混淆中捕获的变量
【发布时间】:2012-07-30 03:45:12
【问题描述】:

我有一个应用程序正在为文明 V 创建地图。作为一个有趣的设计选择,我决定创建几个函数来为我循环遍历地图。这样,我可以将函数指针或 lambda 函数传递给该函数,该函数通过整个地图对每个图块执行某些操作。这背后的原因是,如果我或其他人会更改地图的存储方式(从 2D 数组到 2D 矢量或其他),则只需更改一个函数而不是整个代码库。

现在问题来了,这里有一些代码。

错误代码。

    case ALL_SNOW:
        m.loop_through_limit([] (Tile* t) {
            t = new Snow(t->get_x(), t->get_y()); 
            return t;
        }, x, y, width, height);
        break;
    case PTN_ONE:
        m.loop_through_limit([&] (Tile* t) {
            int cur_x = t->get_x();
            int cur_y = t->get_y();
            t = new Plains(cur_x, cur_y);
            // if (y <= height/4 || y >= (height*3)/4) {
            //     Top quarter rows and bottom quarter rows
            //     t = new Ocean(cur_x, cur_y);
            // } else if (cur_x <= width/4) {
            //     Leftmost columns
            //     t = new Ocean(cur_x, cur_y);
            // } else if (cur_x >= (width*3)/4) {
            //     Rightmost columns
            //     t = new Desert(cur_x, cur_y);
            // } 
            return t;
        }, x, y, width, height);
        break;

头文件中的定义。

void loop_through(void (*)(Tile* t));
void loop_through_limit(Tile* (*)(Tile* t), int start_x, int start_y, int width, int height);

现在,除了注释掉的代码之外,每种情况下的区别并不大。这工作正常。当我注释掉 if 语句块时,这就是我的输出。

c++ -c  -g -O3 -ffast-math -Wall -Weffc++ -std=c++0x -o tile_block.o tile_block.cpp 
tile_block.cpp: In static member function ‘static void TileBlock::write(Map&, TileBlock::Patterns, int, int, int, int)’:
tile_block.cpp:82:35: error: no matching function for call to ‘Map::loop_through_limit(TileBlock::write(Map&, TileBlock::Patterns, int, int, int, int)::<lambda(Tile*)>, int&, int&, int&, int&)’
tile_block.cpp:82:35: note: candidate is:
map.h:26:10: note: void Map::loop_through_limit(Tile* (*)(Tile*), int, int, int, int)
map.h:26:10: note:   no known conversion for argument 1 from ‘TileBlock::write(Map&, TileBlock::Patterns, int, int, int, int)::<lambda(Tile*)>’ to ‘Tile* (*)(Tile*)’

而且我相信当我开始使用我试图通过引用捕获的参数时就会出现问题。然后它开始变成一个“lambda”函数而不仅仅是一个“函数指针”,也许我只是没有得到它。

有什么建议吗?

【问题讨论】:

    标签: c++ lambda c++11


    【解决方案1】:

    如果 C++11 lambda 捕获变量,则它们不是函数指针。您需要的是 std::function,尤其是对于第二个函数,因为用于捕获变量的 lambda。

    所以改变这些:

    void loop_through(void (*)(Tile* t));
    void loop_through_limit(Tile* (*)(Tile* t), /*...*/);
    

    对这些:

    void loop_through(std::function<void(Tile*)>  fun);
    void loop_through_limit(std::function<Tile*(Tile*)> fun, /*...*/);
    

    现在您可以将 lambda 传递给上述函数。

    【讨论】:

      【解决方案2】:

      Lambda 通常被实现为函子(具有重载operator() 的对象)。对于没有捕获的lambas,标准保证它们可以隐式转换为具有相同签名的函数指针(安全,因为lambda仿函数不包含数据)。对于带有捕获的 lambda,这是不安全的,因此被禁止。

      为了实现这一点,您需要将 loop_throughloop_through_limit 方法更改为采用 std::function&lt;void(Tile*)&gt;

      void loop_through(std::function<void(Tile*)>);
      void loop_through_limit(std::function<Tile*(Tile*)> func, int start_x, int start_y, int width, int height);
      

      或模板函数采用任何类型的可执行函数对象

      template<typename F> void loop_through_limit(F func);
      template<typename F> void loop_through_limit(F func, int start_x, int start_y, int width, int height);
      

      后一种方法的优点是开销较低(无需构造std::function 对象),而前一种方法的优点是不使方法成为模板,因此它可以例如仍然是虚拟的。

      【讨论】:

        【解决方案3】:

        ...然后它开始变成一个“lambda”函数,而不仅仅是一个 “函数指针”...

        完全正确,Standard 说不捕获任何内容的 lambda 可以隐式转换为具有相同签名的函数指针。

        您可以做的是制作loop_throughloop_through_limit 模板

        template <typename F>
        void loop_through(F);
        template <typename F>
        void loop_through_limit(F, int start_x, int start_y, int width, int height);
        

        并在里面打电话给f

        【讨论】:

          猜你喜欢
          • 2014-02-04
          • 2013-03-26
          • 1970-01-01
          • 2014-04-14
          • 1970-01-01
          • 2021-02-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多