我有一个为文明V创建地图的应用程序。作为一个有趣的设计选择,我决定创建一些函数来为我循环遍历地图。这样我就可以将函数指针或lambda函数传递给该函数,该函数遍历整个映射,为每个tile执行某些操作。这背后的推理是,如果我或其他人改变地图的存储方式(从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*)’
我相信当我开始使用我试图通过引用捕获的参数时问题就出现了。然后它开始变成一个&#34; lambda&#34;函数而不只是一个&#34;函数指针&#34;,也许我只是没有得到它。
有什么建议吗?
答案 0 :(得分:5)
如果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传递给上面的函数。
答案 1 :(得分:2)
Lambda通常实现为仿函数(具有重载operator()
的对象)。对于没有捕获的lambas,标准保证它们可以隐式转换为具有相同签名的函数指针(安全,因为lambda仿函数不包含数据)。对于没有安全可能并因此被禁止的捕获的lambda。
为了实现此目的,您需要将loop_through
和loop_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
对象)的优点,而前一种方法具有不使该方法成为模板的优点,因此它可以例如仍然是虚拟的。
答案 2 :(得分:1)
...然后它开始变成“lambda”函数而不仅仅是a “函数指针”......
这完全正确,标准说没有捕获任何内容的lambda可以隐式地转换为具有相同签名的函数指针。
您可以做的是制作loop_through
和loop_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
。