13

ほとんどの STL コンテナーで機能する C++ で頻繁に使用する「foreach」マクロがあります。

#define foreach(var, container) \
  for(typeof((container).begin()) var = (container).begin(); \
      var != (container).end(); \
      ++var)

(「typeof」は gcc 拡張であることに注意してください。) 次のように使用されます。

std::vector< Blorgus > blorgi = ...;
foreach(blorgus, blorgi) {
  blorgus->draw();
}

マップの値を反復するようなものを作りたいと思います。おそらく「foreach_value」と呼んでください。だから書く代わりに

foreach(pair, mymap) {
  pair->second->foo();
}

私は書くだろう

foreach_value(v, mymap) {
  v.foo();
}

イテレータと値変数 (上記の「v」) の 2 つの変数を宣言する必要があるため、これを実行するマクロを思い付くことができません。gcc 拡張機能を使用しても、for ループの初期化子でそれを行う方法がわかりません。foreach_value 呼び出しの直前に宣言することもできますが、同じスコープ内の foreach_value マクロの他のインスタンスと競合します。現在の行番号をイテレータ変数名の末尾に付けることができれば機能しますが、その方法がわかりません。

4

11 に答える 11

8

あなたはBOOST_FOREACHを探しているでしょう- 彼らはすでにあなたのためにすべての仕事をしています!

独自にロールしたい場合は、C++ の任意の場所でブロックを宣言できます。これにより、itr->second の中間ストレージに関するスコープの問題が解決されます ...

// Valid C++ code (which does nothing useful)
{
  int a = 21; // Which could be storage of your value type
}
// a out of scope here
{ 
  int a = 32; // Does not conflict with a above
}
于 2008-09-17T00:59:13.117 に答える
5

これは、2 つのループを使用して行うことができます。最初は、コンテナ変数の関数である名前でイテレータを宣言します (独自のコードとの競合が心配な場合は、これを醜くすることができます)。2 番目は値変数を宣言します。

#define ci(container) container ## iter
#define foreach_value(var, container) \
    for (typeof((container).begin()) ci(container) = container.begin(); \
         ci(container) != container.end(); ) \
        for (typeof(ci(container)->second)* var = &ci(container)->second; \
             ci(container) != container.end(); \
             (++ci(container) != container.end()) ? \
                 (var = &ci(container)->second) : var)

同じループ終了条件を使用することで、外側のループは 1 回だけ発生します (運が良ければ、最適化されなくなります)。また、マップが空の場合、イテレータで ->second を呼び出さないようにします。これは、内側のループのインクリメントにおける三項演算子と同じ理由です。最後に、再度参照されないため、var を最後の値のままにします。

ci(container) をインライン化することもできますが、マクロが読みやすくなると思います。

于 2008-09-17T01:48:56.443 に答える
3

STL変換関数も同様のことを行います。

引数は(順番に)次のとおりです。

  1. コンテナの開始を指定する入力イテレータ
  2. コンテナの終了を指定する入力イテレータ
  3. 出力をどこに置くかを定義する出力反復子 (for-each に似たインプレース変換の場合は、#1 で入力反復子を渡すだけです)
  4. 各要素に対して実行する単項関数 (関数オブジェクト)

非常に単純な例として、次のように文字列内の各文字を大文字にすることができます。

#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>

int main(int argc, char* argv[]) {
    std::string s("my lowercase string");
    std::transform(s.begin(), s.end(), s.begin(), toupper);
    std::cout << s << std::endl; // "MY LOWERCASE STRING"
}

あるいは、関数オブジェクトへの呼び出し間でいくつかの値を保持できるようにするcumulate関数もあります。蓄積は、変換の場合のように、入力コンテナー内のデータを変更しません。

于 2008-09-17T02:24:10.603 に答える
1

Boost::For_each は、断然あなたの最善の策です。気の利いたことは、実際に提供されるのはマクロ BOOST_FOREACH() であり、これをラップして、コードで本当に呼び出したいものに #define できることです。ほとんどの人は古き良き「foreach」を選択しますが、他のショップではコーディング基準が異なる場合があるため、これはその考え方に適合します。Boost には、C++ 開発者向けのその他の機能もたくさんあります。使用する価値があります。

于 2008-09-17T02:53:48.410 に答える
1

ローカル変数とポインターの両方で動作する foreach() のいくつかのバリアントと、ループ内から要素を削除しないように保護された追加のバージョンを含む、小さな Foreach.h ヘルパーを作成しました。したがって、私のマクロを使用するコードは、次のように見栄えがよく快適です。

#include <cstdio>
#include <vector>
#include "foreach.h"

int main()
{
    // make int vector and fill it
    vector<int> k;
    for (int i=0; i<10; ++i) k.push_back(i);

    // show what the upper loop filled
    foreach_ (it, k) printf("%i ",(*it));
    printf("\n");

    // show all of the data, but get rid of 4
    // http://en.wikipedia.org/wiki/Tetraphobia :)
    foreachdel_ (it, k)
    {
        if (*it == 4) it=k.erase(it);
        printf("%i ",(*it));
    }
    printf("\n");

    return 0;
}

出力:

0 1 2 3 4 5 6 7 8 9
0 1 2 3 5 6 7 8 9

私のForeach.hは、次のマクロを提供します。

  • foreach() - ポインターの通常の foreach
  • foreach_() - ローカル変数の通常の foreach
  • foreachdel() - ループ内の削除をチェックする foreach バージョン、ポインター バージョン
  • foreachdel_() - ループ内の削除をチェックする foreach バージョン、ローカル変数バージョン

彼らは確かに私のために働きます、私は彼らがあなたの人生を少し楽にしてくれることを願っています:)

于 2010-11-18T12:34:55.250 に答える
1

Boost ライブラリの使用を考えたことはありますか? それらには、おそらくあなたが書くものよりも堅牢なforeachマクロが実装されtransform_iteratorています...そして、あなたが望むものの2番目の抽出部分を行うために使用できるように見えるものもあります.

残念ながら、私は十分な C++ を知らないため、正確な使用方法を説明することはできません :)この Google 検索では、有望な回答がいくつか見つかります: comp.lang.c++.moderatedBoost transform_iterator use case

于 2008-09-17T01:01:11.130 に答える
1

この質問には 2 つの部分があります。どういうわけか (1) マップの値(キーではない) に対してイテレータ (または反復可能なシーケンス) を生成し、(2) マクロを使用して多くのボイラープレートなしで反復を行う必要があります。

最もクリーンな解決策は、パート (1) にBoost Range Adapterを使用し、パート (2) にBoost Foreachを使用することです。マクロを記述したり、反復子を自分で実装したりする必要はありません。

#include <map>
#include <string>
#include <boost/range/adaptor/map.hpp>
#include <boost/foreach.hpp>

int main()
{
    // Sample data
    std::map<int, std::string> myMap ;
    myMap[0] = "Zero" ;
    myMap[10] = "Ten" ;
    myMap[20] = "Twenty" ;

    // Loop over map values
    BOOST_FOREACH( std::string text, myMap | boost::adaptors::map_values )
    {
        std::cout << text << " " ;
    }
}
// Output:
// Zero Ten Twenty
于 2011-08-31T05:48:38.417 に答える
0
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)

C ++にはtypeofはありません...これはどのようにコンパイルされますか?(それは確かにポータブルではありません)

于 2008-09-17T07:01:24.133 に答える
0

私はコードforeach_valueに基づいて自分自身を実装しました:Boost foreach

#include <boost/preprocessor/cat.hpp>
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x)  BOOST_PP_CAT(x, __LINE__)

namespace munzekonza {
namespace foreach_in_map_private {
inline bool set_false(bool& b) {
  b = false;
  return false;
}

}
}

#define MUNZEKONZA_FOREACH_VALUE(value, map)                                  \
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin();      \
        MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();)       \
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true;       \
      MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) &&               \
      MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();          \
      (MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ?              \
        ((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) :          \
        (void)0)                                                              \
  if( munzekonza::foreach_in_map_private::set_false(                          \
          MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else    \
  for( value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second;      \
        !MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue);              \
        MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)        

たとえば、次のようにコードで使用できます。

#define MUNZEKONZA_FOREACH_VALUE foreach_value

std::map<int, std::string> mymap;
// populate the map ...

foreach_value( const std::string& value, mymap ) {
  // do something with value
}

// change value
foreach_value( std::string& value, mymap ) {
  value = "hey";
}
于 2012-08-15T15:32:30.140 に答える
0

mymap の型をテンプレート パラメーターとして受け取り、* と -> をオーバーロードすることで、値に対して反復子のように機能するテンプレート クラスを定義できます。

于 2008-09-17T00:53:55.717 に答える