Search code examples
c++c++11for-loopiteratorranged-loops

Range based for loops and multiple iterators


I have the following code, representing a mesh in a 3D application (some code ommited for clarity):

class Mesh {
public:
    typedef std::vector<Vertex> Vertices;
    typedef std::vector<int> Elements;

    template<class VerticesIt, class ElementsIt>
    Mesh(const VerticesIt verticesBegin,
         const VerticesIt verticesEnd,
         const ElementsIt elementsBegin,
         const ElementsIt elementsEnd) :
    vertices_(verticesBegin, verticesEnd),
    elements_(elementsBegin, elementsEnd) {
    }        
    virtual ~Mesh();

    typename Vertices::const_iterator verticesBegin() const {
        return vertices_.begin();
    };

    typename Vertices::const_iterator verticesEnd() const {
        return vertices_.end();
    };

    typename Elements::const_iterator elementsBegin() const {
        return elements_.begin();
    };

    typename Elements::const_iterator elementsEnd() const {
        return elements_.end();
    };

private:       
    Vertices vertices_;
    Elements elements_;

};

It works quite nicely, providing a clear interface to the internal data. No implementation details are exposed for the containers.

I have one little hiccup regarding this though. I can not use range based for loops, iterators has to be used:

for (auto it = mesh.verticesBegin(); it != mesh.verticesEnd(); ++it) {
// Do stuff
}

for (auto it = mesh.elementsBegin(); it != mesh.elementsEnd(); ++it) {
// Do stuff
}

A bit verbose for my taste. My preferred client code would instead look like this:

for(auto & vert : mesh.vertices) {
// Do stuff.
}

for(auto & element : mesh.elements) {
// Do stuff.
}

Is it possible to achieve this without exposing implementation details of the containers? Also, I would not like to wrap the containers into custom classes, since I want full access to the chosen container (std::vector) inside the Mesh class.


Solution

  • You could use some sort of proxy, such as this:

    template<typename Container>
    class ConstIteratorProxy
    {
    public:
        ConstIteratorProxy(const Container& container) : container_(container) { }
        typename Container::const_iterator begin() const {
            return container_.begin();
        };
        typename Container::const_iterator end() const {
            return container_.end();
        };
    private:
        const Container& container_;
    };
    

    And in Mesh:

    ConstIteratorProxy<Vertices> vertices() const {
        return ConstIteratorProxy<Vertices>(vertices_);
    }
    ConstIteratorProxy<Elements> elements() const {
        return ConstIteratorProxy<Elements>(elements_);
    }
    

    Then to use it:

    Mesh m;
    for (auto& v : m.vertices()) { }
    for (auto& e : m.elements()) { }