Я могу преобразовать обратный итератор во вперед итератор?

Мне назвали класс Action, который является по существу оберткой вокруг двухсторонней очереди Move объекты.

Поскольку я должен пересечь двухстороннюю очередь Moves оба передают и назад, я имею вперед итератор и reverse_iterator как членские переменные класса. Причиной этого является becuase, который я должен знать, когда я пошел одно прошлое "конец" двухсторонней очереди, оба, когда я - продвижения или назад.

Класс похож на это:

class Action
{
public:
    SetMoves(std::deque<Move> & dmoves) { _moves = dmoves; }
    void Advance();
    bool Finished() 
    {
        if( bForward )
            return (currentfwd==_moves.end());
        else
            return (currentbck==_moves.rend());
    }
private:
    std::deque<Move> _moves;
    std::deque<Move>::const_iterator currentfwd;
    std::deque<Move>::const_reverse_iterator currentbck;
    bool bForward;
};

Advance функция следующие:

void Action::Advance
{
    if( bForward)
        currentfwd++;
    else
        currentbck++;
}

Моя проблема, я хочу быть в состоянии получить итератор к току Move объект, не будучи должен запросить, являюсь ли я продвижениями или назад. Это означает одну функцию, возвращая один тип итератора, но у меня есть два типа.

Если я забываю возвращать итератор и возвращаю ссылку константы на a Move объект вместо этого?

всего наилучшего,

BeeBand

46
задан BeeBand 10 January 2010 в 17:52
поделиться

5 ответов

Это точно вид проблемы, которая запросила дизайн STL запускаться с. Существуют настоящие причины для:

  1. Не итераторы хранения наряду с контейнерами
  2. Используя алгоритмы, которые принимают произвольные итераторы
  3. , алгоритмы Наличия оценивают весь диапазон вместо единственного предмета за один раз

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

Для тех, кто заботится, прежде всего, о вопросе в заголовке, ответ в большой степени квалифицирован "да". В частности, reverse_iterator имеет основа () член, чтобы сделать это. Квалификации несколько проблематичны все же.

демонстрирование проблемы, рассмотрите код как это:

#include <iostream>
#include <vector>
#include <iterator>

int main() { 
    int i[] = { 1, 2, 3, 4};
    std::vector<int> numbers(i, i+4);

    std::cout << *numbers.rbegin() << "\n";
    std::cout << *numbers.rbegin().base() << "\n";
    std::cout << *(numbers.rbegin()+1).base() << "\n";

    std::cout << *numbers.rend() << "\n";
    std::cout << *numbers.rend().base() << "\n";
    std::cout << *(numbers.rend()+1).base() << "\n";
}

Выполнение этого в данный момент на моей особой машине производит следующий вывод:

4
0
4
-1879048016
1
-1879048016

Сводка: с rbegin () мы должны добавлять тот прежде, чем преобразовать во вперед итератор для получения итератора, это допустимо - но с разрывают () , мы должны не , добавляет тот прежде, чем преобразовать для получения допустимого итератора.

, пока вы используете X.rbegin () и X.rend () как параметры к универсальному алгоритму, это прекрасно - но опыт указывает, что преобразование для передачи итераторов часто приводит к проблемам.

В конце, однако, для корпуса вопроса (в противоположность заголовку), ответ в значительной степени как дан выше: проблема происходит от попытки создать объект, который комбинирует набор с несколькими итераторами в тот набор. Решить ту проблему и целый бизнес с вперед и инвертировать итераторы, становится спорным.

29
ответ дан 26 November 2019 в 20:19
поделиться

Поскольку std :: deque является контейнером произвольного доступа (то же самое, что std :: vector ) гораздо лучше использовать один целочисленный индекс в двухсторонней очереди для обоих обходов.

6
ответ дан 26 November 2019 в 20:19
поделиться

Может, тебе стоит переосмыслить свой выбор контейнера.

Обычно вам не нужно использовать реверсивные итераторы, чтобы идти назад,

currentfwd--

будет идти назад, хотя это может не сработать (что, я полагаю, вы и пытались сделать) с помощью dequeue.

Что вам действительно следует сделать, так это смоделировать ваш класс здесь в качестве декоратора dequeue и реализовать свои собственные Action-итераторы. Это было бы то, что я бы сделал в любом случае.

0
ответ дан 26 November 2019 в 20:19
поделиться

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

И я хочу использовать $! --- & при вызове методов! Разберись. Если вы собираетесь написать код C++, придерживайтесь условных обозначений C++. И очень важным соглашением C++ является предпочтение функций, не являющихся членами, когда это возможно.

Есть причина, по которой гуру C++ рекомендуют это:

Это улучшает инкапсуляцию, расширяемость и повторное использование. ( std:: sort может работать со всеми парами итераторов , поскольку не является членом какого-либо одного класса итераторов или контейнеров. И как бы вы ни расширяли std:: Последовательностью , вы не можете ломать его, пока придерживаетесь не-членских функций. И даже если у вас нет доступа к исходному коду класса или вам не разрешено его изменять, вы все равно можете расширить его, определив функции, не являющиеся членами класса)

Лично я не вижу точки в вашем коде. Разве это не намного проще, понятнее и короче?

string OperateOnString( float num, string a, string b )
{
    string nameS;
    Format(nameS, "%f-%s-%s", num, a.c_str(), b.c_str() );
    return nameS;
}

// or even better, if `Format` is made to return the string it creates, instead of taking it as a parameter
string OperateOnString( float num, string a, string b )
{
    return Format("%f-%s-%s", num, a.c_str(), b.c_str() );
}

Когда в Риме, делайте, как римляне, как говорится. Особенно , когда у римлян есть веские причины поступать так, как у них есть. И особенно когда ваш собственный способ сделать это на самом деле не имеет ни одного преимущества. Он более подвержен ошибкам, сбивает с толку людей, читающих ваш код, не является идиоматическим, и это просто больше строк кода, чтобы сделать то же самое.

Что касается вашей проблемы, трудно найти функции, не являющиеся членами, которые расширяют последовательности , поместите их в пространство имен, если это связано. Для этого они и нужны. Создайте пространство имен StringUtil или что-то подобное и поместите их туда.

-121--3765849-

Изменение ответа Фредди при использовании MVC - пришлось использовать:

HttpContext.User.Identity.Name

Незначительное изменение, но я подумал, что опубликую его в случае, если кто-то еще попытается сделать это в MVC попадет в ту же загвоздку.

-121--2890584-

Обратные итераторы имеют члена base () , который возвращает соответствующий прямой итератор. Обратите внимание, что не является итератором, который ссылается на один и тот же объект - он фактически ссылается на следующий объект в последовательности. Это так, что rbegin () соответствует end () и rend () соответствует begin () .

Так что если вы хотите вернуть итератор, то вы бы сделали что-то вроде

std::deque<Move>::const_iterator Current() const
{
    if (forward)
        return currentfwd;
    else
        return (currentbck+1).base();
}

Я бы предпочел вернуть ссылку, хотя, и инкапсулировать все итерационные детали внутри класса.

65
ответ дан 26 November 2019 в 20:19
поделиться

Мне кажется, что у вас на самом деле есть два разных поведения в одном классе.

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

Лично я все для разоблачения обеих итераторов (т. Е. Вперед начинается, конец, rbegin и Rind ).

Вы также можете вернуть простой объект ITERATOR:

template <class T>
class Iterator
{
public:
  typedef typename T::reference_type reference_type;

  Iterator(T it, T end) : m_it(it), m_end(end) {}

  operator bool() const { return m_it != m_end; }

  reference_type operator*() const { return *m_it; }

  Iterator& operator++() { ++m_it; return *this; }

private:
  T m_it;
  T m_end;
};

template <class T>
Iterator<T> make_iterator(T it, T end) { return Iterator<T>(it,end); }

затем вы можете просто вернуть этот простой объект:

class Action
{
public:
  Action(std::deque<Move> const& d): m_deque(d) {} // const& please

  typedef Iterator< std::deque<Move>::iterator > forward_iterator_type;
  typedef Iterator< std::deque<Move>::reverse_iterator > backward_iterator_type;

  forward_iterator_type forward_iterator()
  {
    return make_iterator(m_deque.begin(), m_deque.end());
  }

  backward_iterator_type backward_iterator()
  {
    return make_iterator(m_deque.rbegin(), m_deque.rend());
  }


private:
  std::deque<Move> m_deque;
};

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

Но на самом деле, я на самом деле не вижу смысла хранения как переадресации, так и назад и обратного итератора, если окажется, что вы будете использовать только один: /

1
ответ дан 26 November 2019 в 20:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: