Написание итератора, который заставляет несколько контейнеров выглядеть как один - PullRequest
20 голосов
/ 19 июля 2011

Рассмотрим следующий упрощенный пример и желаемый результат:

class A
{
    class combined_iterator
    {
        ????
    }
    typedef ??? t_combined_it;

    t_combined_it begin();
    t_combined_it end();

    std::vector<int> m_Vec1, m_Vect2;
}

A a;
a.m_Vec1.push_back(1);
a.m_Vec2.push_back(2);
for (A::t_combined_it it = a.begin() ; it != a.end() ; it++) {
     std::cout << *it << " ";
}

Выход:

1 2 

Я думаю, что вопрос ясен из этого: как мне написать итератор, который бы выглядел так, как будто два или более других итераторов действительно являются одной последовательностью. Так что в этом примере вместо итерации по m_Vec1 и m_Vec2 я могу использовать итератор, который перебирает сначала элементы m_Vec1, а затем m_Vec2, прозрачно.

Я нашел следующий вопрос, который, я думаю, задает тот же вопрос: Создайте итератор c ++, который проходит через 2 контейнера . На этот вопрос не было хороших ответов; решение, представленное первоначальным автором, кажется запутанным, и оно (относительно) требует большого объема памяти.

Я попробовал наивный подход, сохранив std :: vector :: iterator в качестве члена моего пользовательского итератора и сравнив его с итераторами .end () каждой из итерируемых последовательностей; однако кажется, что нельзя сравнивать итераторы из разных контейнеров (где я бы предпочел, чтобы они просто возвращали «не равно» - может быть, это направление для поиска решения этой проблемы? Я не могу думать о том, как реализовать это, хотя).

Где это возможно и, если это уместно, я бы хотел использовать итераторы boost ::, поскольку я использую их в других местах, и мне нравится однородность, которую он обеспечивает для моих реализаций итераторов; но, конечно, если у кого-то есть идея, не используя их, я могу использовать их в себе, поэтому они не нужны в этом смысле.

Ответы [ 6 ]

19 голосов
/ 19 июля 2011

boost :: join - это то, что вы ищете. Вы также можете изучить реализацию, в частности, как получить наименьший общий знаменатель для типов обхода контейнера, ссылки и возвращаемого значения. Цитировать:

Назначение функции соединения - объединить два диапазона в один более длинный.

Результирующий диапазон будет иметь наименьший общий обход из двух диапазонов, представленных в качестве параметров.

Обратите внимание, что объединенный диапазон влечет за собой затраты производительности из-за необходимости проверки, достигнут ли конец диапазона> внутри во время обхода.

7 голосов
/ 19 июля 2011

Я думаю, что ваш "наивный" подход должен работать со следующим изменением: вместо сравнения итератора с end() каждого контейнера, оставьте указатель на текущий контейнер и сравните только итератор текущий контейнер end(). Когда конец достигнут, переходите к следующему контейнеру. Таким образом, вы никогда не сравните итератор с другим контейнером, кроме того, на который он указывает. Это также легко обобщает коллекцию произвольного размера.

3 голосов
/ 19 июля 2011

Я уже реализовал нечто подобное для другого вопроса.Реализация здесь .Основная идея - та, к которой вы подошли: сохранить два диапазона итераторов, когда вас попросят выполнить операцию, чтобы проверить, завершили ли вы итерацию в первом диапазоне, и использовать любой диапазон.

Это ни в коем случае не потокобезопасное средство, и я никогда не использовал эту реализацию в реальном коде, поэтому могут быть проблемы, ошибки и ...

3 голосов
/ 19 июля 2011

zip_iterator работает, если вы хотите выполнить итерацию двух итераторов параллельно. Если вам нужно последовательно перебирать контейнеры, вы, вероятно, можете реализовать один из них, используя iterator_adaptor .

1 голос
/ 22 декабря 2012

Ответы, которые другие люди давали, работают только для объединения двух диапазонов, но не произвольного числа диапазонов.

PStade Oven имеет concatenated адаптер присоединение любое количество диапазонов;он также имеет адаптер jointed для объединения двух диапазонов, в частности.

Конечно, вы можете превратить диапазоны в итераторы с помощью begin() и end(), если это необходимо;просто убедитесь, что диапазоны превышают итераторы.

0 голосов
/ 19 июля 2011

Аасмунд прав. Ваш подход, хотя и простой, должен работать. Но есть много возможностей для оптимизации.

Коллекции Объекты часто бывают большими, и поэтому для их повторения требуется некоторое время. Особенно списки и массивы.

Вы должны рассмотреть многопоточность, чтобы каждая коллекция могла повторяться параллельно.

Дополнительно. Вам следует воспользоваться советом поста, на который вы ссылаетесь, чтобы использовать и использовать

Boost.MultiIndex

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...