48

私は C/C++ プログラミングは初めてですが、C# でプログラミングを始めて 1 年半になります。私はC#が好きで、Listクラスが好きなので、練習としてC++でListクラスを作ってみようと思いました。

List<int> ls;
int whatever = 123;
ls.Add(1);
ls.Add(235445);
ls.Add(whatever);

実装は、そこにある任意の Array List クラスに似ています。アイテムを保管するメンバーがいて、T* vectorこのストレージがいっぱいになりそうになったら、サイズを変更します。

これは本番環境では使用しないことに注意してください。これは単なる演習です。私はよく知っていてvector<T>、友達です。

ここで、リストの項目をループしたいと思います。私は使いたくないfor(int i=0;i<n; i==)。Visual Studio で入力forし、Intellisense を待っていたところ、次のように提案されました。

for each (object var in collection_to_loop)
{

}        

これは明らかに、私の List 実装では機能しません。マクロマジックができると思ったのですが、これは大ハックのように感じます。実際、私が最も気になるのは、次のような型を渡すことです。

#define foreach(type, var, list)\
int _i_ = 0;\
##type var;\
for (_i_ = 0, var=list[_i_]; _i_<list.Length();_i_++,var=list[_i_]) 

foreach(int,i,ls){
    doWork(i);
}

foreach-like私の質問は: このカスタム List クラスをループで動作させる方法はありますか?

4

5 に答える 5

40

iterable型にしましょうIterable。では、作るためには

for (Type x : iterable)

コンパイルするには、呼び出される型が必要でTypeありIType、関数が必要です

IType Iterable::begin()
IType Iterable::end()

IType機能を提供する必要があります

Type operator*()
void operator++()
bool operator!=(IType)

全体の構造は、次のようなもののための本当に洗練された構文糖衣です

for (IType it = iterable.begin(); it != iterable.end(); ++it) {
    Type x = *it;
    ...
}

の代わりにType、互換性のある任意の型 (const Typeまたは などType&) を使用できます。これには、期待される意味 (constness、コピーではなく参照など) があります。

全体の展開は構文的に行われるため、演算子の宣言を少し変更することもできます。たとえば、 *it が参照を返すようにしたりconst IType& rhs、必要に応じて != を取得したりすることができます。

for (Type& x : iterable)が参照を返さない場合はフォームを使用できないことに注意してください*it(ただし、参照を返す場合は、コピー バージョンを使用することもできます)。

また、 は演算子の前置バージョンをoperator++()定義することに注意してください。ただし、明示的に後置を定義しない限り、後置演算子としても使用されます。範囲指定された for は、 postfix のみを指定するとコンパイルされません。これは btw.can として宣言できます(ダミーの int 引数)。++++++operator++(int)


最小限の実例:

#include <stdio.h>
typedef int Type;

struct IType {
    Type* p;
    IType(Type* p) : p(p) {}
    bool operator!=(IType rhs) {return p != rhs.p;}
    Type& operator*() {return *p;}
    void operator++() {++p;}
};

const int SIZE = 10;
struct Iterable {
    Type data[SIZE];

    IType begin() {return IType(data); }
    IType end() {return IType(data + SIZE);}
};

Iterable iterable;

int main() {
    int i = 0;
    for (Type& x : iterable) {
        x = i++;
    }
    for (Type x : iterable) {
        printf("%d", x);
    }
}

出力

0123456789

次のマクロを使用して、ranged-for-each (たとえば、古い C++ コンパイラ用) を偽造できます。

 #define ln(l, x) x##l // creates unique labels
 #define l(x,y)  ln(x,y)
 #define for_each(T,x,iterable) for (bool _run = true;_run;_run = false) for (auto it = iterable.begin(); it != iterable.end(); ++it)\
     if (1) {\
         _run = true; goto l(__LINE__,body); l(__LINE__,cont): _run = true; continue; l(__LINE__,finish): break;\
         } else\
            while (1)   \
                if (1) {\
                    if (!_run) goto l(__LINE__,cont);/* we reach here if the block terminated normally/via continue */   \
                    goto l(__LINE__,finish);/* we reach here if the block terminated by break */\
                }   \
                else\
                l(__LINE__,body): for (T x = *it;_run;_run=false) /* block following the expanded macro */                         

 int main() {
     int i = 0;
     for_each(Type&, x, iterable) {
         i++;
         if (i > 5) break;
         x = i;
     }
     for_each(Type, x, iterable) {
         printf("%d", x);
     }
     while (1);
 }

(コンパイラに auto がない場合は、declspec を使用するか、IType を渡します)。

出力:

 1234500000

ご覧のとおり、複雑な構造のおかげでこれで動作しますcontinue。カスタム制御構造を作成するための C プリプロセッサのハッキングについては、 http://www.chiark.greenend.org.uk/~sgtatham/mp/breakを参照してください。

于 2016-04-13T18:38:54.633 に答える