How to make my custom type to work with “range-based for loops”?

后端 未结 9 2176
长情又很酷
长情又很酷 2020-11-22 08:06

Like many people these days I have been trying the different features that C++11 brings. One of my favorites is the \"range-based for loops\".

I understand that:

相关标签:
9条回答
  • 2020-11-22 08:44

    I write my answer because some people might be more happy with simple real life example without STL includes.

    I have my own plain only data array implementation for some reason, and I wanted to use the range based for loop. Here is my solution:

     template <typename DataType>
     class PodArray {
     public:
       class iterator {
       public:
         iterator(DataType * ptr): ptr(ptr){}
         iterator operator++() { ++ptr; return *this; }
         bool operator!=(const iterator & other) const { return ptr != other.ptr; }
         const DataType& operator*() const { return *ptr; }
       private:
         DataType* ptr;
       };
     private:
       unsigned len;
       DataType *val;
     public:
       iterator begin() const { return iterator(val); }
       iterator end() const { return iterator(val + len); }
    
       // rest of the container definition not related to the question ...
     };
    

    Then the usage example:

    PodArray<char> array;
    // fill up array in some way
    for(auto& c : array)
      printf("char: %c\n", c);
    
    0 讨论(0)
  • 2020-11-22 08:46

    I would like to elaborate some parts of @Steve Jessop's answer, for which at first I didn't understand. Hope it helps.

    std::begin calls the begin() member function anyway, so if you only implement one of the above, then the results should be the same no matter which one you choose. That's the same results for ranged-based for loops, and also the same result for mere mortal code that doesn't have its own magical name resolution rules so just does using std::begin; followed by an unqualified call to begin(a).

    If you implement the member functions and the ADL functions, though, then range-based for loops should call the member functions, whereas mere mortals will call the ADL functions. Best make sure they do the same thing in that case!


    https://en.cppreference.com/w/cpp/language/range-for :

    • If ...
    • If range_expression is an expression of a class type C that has both a member named begin and a member named end (regardless of the type or accessibility of such member), then begin_expr is __range.begin() and end_expr is __range.end();
    • Otherwise, begin_expr is begin(__range) and end_expr is end(__range), which are found via argument-dependent lookup (non-ADL lookup is not performed).

    For range-based for loop, member functions are selected first.

    But for

    using std::begin;
    begin(instance);
    

    ADL functions are selected first.


    Example:

    #include <iostream>
    #include <string>
    using std::cout;
    using std::endl;
    
    namespace Foo{
        struct A{
            //member function version
            int* begin(){
                cout << "111";
                int* p = new int(3);  //leak I know, for simplicity
                return p;
            }
            int *end(){
                cout << "111";
                int* p = new int(4);
                return p;
            }
        };
    
        //ADL version
    
        int* begin(A a){
            cout << "222";
            int* p = new int(5);
            return p;
        }
    
        int* end(A a){
            cout << "222";
            int* p = new int(6);
            return p;
        }
    
    }
    
    int main(int argc, char *args[]){
    //    Uncomment only one of two code sections below for each trial
    
    //    Foo::A a;
    //    using std::begin;
    //    begin(a);  //ADL version are selected. If comment out ADL version, then member functions are called.
    
    
    //      Foo::A a;
    //      for(auto s: a){  //member functions are selected. If comment out member functions, then ADL are called.
    //      }
    }
    
    0 讨论(0)
  • 2020-11-22 08:48

    Chris Redford's answer also works for Qt containers (of course). Here is an adaption (notice I return a constBegin(), respectively constEnd() from the const_iterator methods):

    class MyCustomClass{
        QList<MyCustomDatatype> data_;
    public:    
        // ctors,dtor, methods here...
    
        QList<MyCustomDatatype>::iterator begin() { return data_.begin(); }
        QList<MyCustomDatatype>::iterator end() { return data_.end(); }
        QList<MyCustomDatatype>::const_iterator begin() const{ return data_.constBegin(); }
        QList<MyCustomDatatype>::const_iterator end() const{ return data_.constEnd(); }
    };
    
    0 讨论(0)
提交回复
热议问题