How to make the for each loop function in C++ work with a custom class

前端 未结 5 1230
遇见更好的自我
遇见更好的自我 2020-12-05 04:18

I\'m new to C/C++ programming, but I\'ve been programming in C# for 1.5 years now. I like C# and I like the List class, so I thought about making a List class in C++ as an e

5条回答
  •  抹茶落季
    2020-12-05 04:27

    Let iterable be of type Iterable. Then, in order to make

    for (Type x : iterable)
    

    compile, there must be types called Type and IType and there must be functions

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

    IType must provide the functions

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

    The whole construction is really sophisticated syntactic sugar for something like

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

    where instead of Type, any compatible type (such as const Type or Type&) can be used, which will have the expected implications (constness, reference-instead-of-copy etc.).

    Since the whole expansion happens syntactically, you can also change the declaration of the operators a bit, e.g. having *it return a reference or having != take a const IType& rhs as needed.

    Note that you cannot use the for (Type& x : iterable) form if *it does not return a reference (but if it returns a reference, you can also use the copy version).

    Note also that operator++() defines the prefix version of the ++ operator -- however it will also be used as the postfix operator unless you explicitly define a postfix ++. The ranged-for will not compile if you only supply a postfix ++, which btw.can be declared as operator++(int) (dummy int argument).


    Minimal working example:

    #include 
    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);
        }
    }
    

    output

    0123456789
    

    You can fake the ranged-for-each (e.g. for older C++ compilers) with the following macro:

     #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);
     }
    

    (use declspec or pass IType if your compiler doesn't even have auto).

    Output:

     1234500000
    

    As you can see, continue and break will work with this thanks to its complicated construction. See http://www.chiark.greenend.org.uk/~sgtatham/mp/ for more C-preprocessor hacking to create custom control structures.

提交回复
热议问题