1

文明 V のマップを作成するアプリケーションがあります。興味深いデザインの選択として、マップをループ処理する関数をいくつか作成することにしました。このようにして、関数ポインターまたはラムダ関数をその関数に渡し、マップ全体を通過して各タイルに何かを行うことができます。この背後にある理由は、私または他の誰かがマップの保存方法を (2D 配列から 2D ベクトルなどに) 変更した場合、コードベース全体ではなく 1 つの関数のみを変更する必要があるということです。

問題は、ここにいくつかのコードがあります。

エラーコード。

    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*)’

そして、参照によってキャプチャしようとしているパラメーターの使用を開始すると、問題が発生すると思います。次に、単なる「関数ポインター」ではなく「ラムダ」関数に変わり始めます。おそらく、私はそれを取得していません。

助言がありますか?

4

3 に答える 3

5

C++11 ラムダは、変数をキャプチャする場合、関数ポインターではありません。必要なstd::functionのは、特に 2 番目の関数の場合、そのキャプチャ変数のラムダが呼び出されるためです。

したがって、これらを変更します。

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, /*...*/);

これで、ラムダを上記の関数に渡すことができます。

于 2012-07-29T19:30:34.480 に答える
2

ラムダは通常、ファンクター (オーバーロードされた を持つオブジェクトoperator()) として実装されます。キャプチャのないラムバの場合、標準では、同じシグネチャを持つ関数ポインタに暗黙的に変換できることが保証されています (ラムダ ファンクタにはデータが含まれていないため、安全です)。安全に実行できないため禁止されているキャプチャ付きラムダの場合。

これを可能にするには、loop_throughandloop_through_limitメソッドを take のいずれかに変更する必要があり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オブジェクトを作成する必要がない) という利点がありますが、前者のアプローチには、メソッドをテンプレートにしないという利点があるため、たとえば仮想のままにすることができます。

于 2012-07-29T19:31:39.250 に答える
1

...その後、単なる「関数ポインタ」ではなく「ラムダ」関数に変わり始めます...

その通りです。Standard は、何もキャプチャしないラムダは、同じシグネチャを持つ関数ポインターに暗黙的にキャストできると述べています。

あなたができることはmakeloop_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

于 2012-07-29T19:30:08.877 に答える