基于循环和多个迭代器的范围

时间:2014-08-03 14:35:58

标签: c++ c++11 for-loop iterator ranged-loops

我有以下代码,表示3D应用程序中的网格(为清晰起见,省略了一些代码):

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_;

};

它工作得非常好,为内部数据提供了清晰的界面。没有公开容器的实现细节。

但是,我对此有一点打嗝。我不能使用基于范围的循环,必须使用迭代器:

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

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

我的口味有点冗长。我首选的客户端代码将如下所示:

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

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

是否有可能实现没有暴露容器的实现细节?另外,我不想将容器包装到自定义类中,因为我想要在Mesh类中完全访问所选容器(std :: vector)。

4 个答案:

答案 0 :(得分:9)

您可以使用某种代理,例如:

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_;
};

在Mesh中:

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

然后使用它:

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

答案 1 :(得分:2)

将您的功能verticesBeginverticesEnd分别重命名为beginend。然后你就可以这样写:

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

请注意,基于范围的for循环期望您的收件人具有beginend作为成员函数 - 或者自由函数beginend你的容器作为参数。

另请注意,基于范围的for循环不能迭代两件事 - mesh可以表现得像一个单独的容器,而不是两个容器。因此,如果你想迭代索引,那么你就要暴露它,或者抽象它然后再显示顶点。

或者你可以编写一个zip迭代器,它将在一个for循环中同时迭代两个容器。

答案 2 :(得分:0)

使用代理存储两个迭代器并实现begin和end方法非常容易:

#include <iostream>
#include <vector>

template <typename T>
struct PairIt {
    PairIt(T&& b, T&& e) : b_{std::forward<T>(b)}, e_{std::forward<T>(e)} {}
    T begin() const { return b_; }
    T end() const { return e_; }
private:
    T b_,e_;
};

std::vector<int> badglobal { 1, 2, 3 };

PairIt<std::vector<int>::iterator> ForRangeProxy() {
    return { begin(badglobal), end(badglobal) };
};

int main() {
    for( auto v : ForRangeProxy() )
        std::cout << v << std::endl;
}

您可以为对象想要授予访问权限的每个集合实现多个ForRangeProxy

答案 3 :(得分:0)

您可以从vertices()elements()函数返回迭代器包装器,您可以将它传递给for循环,例如。

template<typename T>
class iterator_wrapper
{
public:
  iterator_wrapper(const typename std::vector<T>::const_iterator &begin_,
                   const typename std::vector<T>::const_iterator &end_)
    :m_begin(begin_), m_end(end_) {}
  typename std::vector<T>::const_iterator begin() const {return m_begin;}
  typename std::vector<T>::const_iterator end() const {return m_end;}

private:
  typename std::vector<T>::const_iterator m_begin, m_end;
};

在网格类中定义vertices()elements()函数:

iterator_wrapper<Vertex> vertices() {return iterator_wrapper<Vertex>(vertices_.begin(), vertices_.end());}
iterator_wrapper<int> elements()    {return iterator_wrapper<int>(elements_.begin(), elements_.end());}

然后称之为:

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

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