60

I am writing an iterator for a container which is being used in place of a STL container. Currently the STL container is being used in many places with the c++11 foreach syntax eg: for(auto &x: C). We have needed to update the code to use a custom class that wraps the STL container:

template< typename Type>
class SomeSortedContainer{
    std::vector<typename Type> m_data; //we wish to iterate over this
    //container implementation code
};    
class SomeSortedContainerIterator{
    //iterator code
};

How do I get auto to use the correct iterator for the custom container so the code is able to be called in the following way?:

SomeSortedContainer C;
for(auto &x : C){
    //do something with x... 
}

In general what is required to ensure that auto uses the correct iterator for a class?

shuttle87
  • 15,466
  • 11
  • 77
  • 106
  • If you are using Visual Studio, you can hover over the variable's name to see its type. IIRC, it shows the actual type, not `auto`. – Cole Tobin Mar 13 '13 at 14:41

4 Answers4

53

To be able to use range-based for, your class should provide const_iterator begin() const and const_iterator end() const members. You can also overload the global begin function, but having a member function is better in my opinion. iterator begin() and const_iterator cbegin() const are also recommended, but not required. If you simply want to iterate over a single internal container, that's REALLY easy:

template< typename Type>
class SomeSortedContainer{

    std::vector<Type> m_data; //we wish to iterate over this
    //container implementation code
public:
    typedef typename std::vector<Type>::iterator iterator;
    typedef typename std::vector<Type>::const_iterator const_iterator;

    iterator begin() {return m_data.begin();}
    const_iterator begin() const {return m_data.begin();}
    const_iterator cbegin() const {return m_data.cbegin();}
    iterator end() {return m_data.end();}
    const_iterator end() const {return m_data.end();}
    const_iterator cend() const {return m_data.cend();}
};    

If you want to iterate over anything custom though, you'll probably have to design your own iterators as classes inside your container.

class const_iterator : public std::iterator<random_access_iterator_tag, Type>{
    typename std::vector<Type>::iterator m_data;
    const_iterator(typename std::vector<Type>::iterator data) :m_data(data) {}
public:
    const_iterator() :m_data() {}
    const_iterator(const const_iterator& rhs) :m_data(rhs.m_data) {}
     //const iterator implementation code
};

For more details on writing an iterator class, see my answer here.

Community
  • 1
  • 1
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
52

You have two choices:

  • you provide member functions named begin and end that can be called like C.begin() and C.end();
  • otherwise, you provide free functions named begin and end that can be found using argument-dependent lookup, or in namespace std, and can be called like begin(C) and end(C).
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • 2
    See Stroustrup's [C++11 FAQ](http://www.stroustrup.com/C++11FAQ.html) for a detailed description of the ["Range-for" statement](http://www.stroustrup.com/C++11FAQ.html#for) (including member/function precedence). – rluba Mar 27 '13 at 09:04
8

As others have stated, your container must implement begin() and end() functions (or have global or std:: functions that take instances of your container as parameters).

Those functions must return the same type (usually container::iterator, but that is only a convention). The returned type must implement operator*, operator++, and operator!=.

Michael Dorst
  • 8,210
  • 11
  • 44
  • 71
dspeyer
  • 2,904
  • 1
  • 18
  • 24
2

To my knowledge SomeSortedContainer just needs to provide begin() and end(). And these should return a standard compliant forward iterator, in your case SomeSortedContainerIterator, which would actually wrap a std::vector<Type>::iterator. With standard compliant I mean it has to provide the usual increment and dereferencing operators, but also all those value_type, reference_type, ... typedefs, which in turn are used by the foreach construct to determine the underlying type of the container elements. But you might just forward them from the std::vector<Type>::iterator.

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • 3
    If you lack the `begin` and `end` member functions then the foreach can also use the `begin` and `end` non-member functions. –  Sep 26 '11 at 23:26
  • @Mike Do you mean free functions taking the container as a single parameter? Nice, I didn't know that. Useful for extending existing container classes, I guess. – Christian Rau Sep 26 '11 at 23:28