lambda函数中捕获的变量混淆

Captured variables in lambda functions confusion

本文关键字:变量 函数 lambda      更新时间:2023-10-16

我有一个应用程序,正在为文明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++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传递给上述函数。

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

为了实现这一点,您需要将loop_throughloop_through_limit方法更改为std::function<void(Tile*)>:

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对象),而前一种方法具有不使方法成为模板的优点,因此它例如仍然可以是虚拟的。

然后它开始变成一个"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);

并在内部调用CCD_ 9。