Диапазон на основе циклов и нескольких итераторов

У меня есть следующий код, представляющий сетку в 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.
}

Можно ли этого добиться без раскрыть детали реализации контейнеров? Кроме того, я не хотел бы заключать контейнеры в пользовательские классы, так как мне нужен полный доступ к выбранному контейнеру (std :: vector) внутри класса Mesh.

4

Решение

Вы можете использовать какой-нибудь прокси, такой как этот:

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

И в сетке:

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()) { }
9

Другие решения

Переименуйте свои функции verticesBegin а также verticesEnd в begin а также end соответственно. Тогда вы сможете написать это:

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

Обратите внимание, что диапазон основан for цикл ожидает, что ваш контейнер содержит begin а также end как функции-члены — или свободные функции begin а также end который принимает ваш контейнер в качестве аргумента.

Также обратите внимание, что основанный на диапазоне цикл for не может перебирать две вещи: mesh может вести себя как один контейнер, а не как два. Так что, если вы хотите перебирать и индексы, то вы должны выставить их или абстрагировать, а затем вместо этого выставить вершины.

Или вы можете написать zip-итератор, который будет перебирать оба контейнера одновременно в одном цикле for.

2

Это довольно просто с прокси-сервером, хранящим два итератора с реализованным методом 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 для каждой коллекции ваш объект хочет предоставить доступ.

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())
{
//....
}
0
По вопросам рекламы [email protected]