Мне назвали класс 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
Это точно вид проблемы, которая запросила дизайн STL запускаться с. Существуют настоящие причины для:
, я подозреваю то, что вы видите, прямо сейчас более или менее верхушка айсберга настоящих проблем. Мой совет состоял бы в том, чтобы предпринять шаги назад, и вместо того, чтобы спросить о том, как иметь дело с деталями дизайна, поскольку он в настоящее время стоит, задайте несколько более общий вопрос о том, что вы пытаетесь выполнить, и как лучше всего выполнить тот конечный результат.
Для тех, кто заботится, прежде всего, о вопросе в заголовке, ответ в большой степени квалифицирован "да". В частности, 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 ()
как параметры к универсальному алгоритму, это прекрасно - но опыт указывает, что преобразование для передачи итераторов часто приводит к проблемам.
В конце, однако, для корпуса вопроса (в противоположность заголовку), ответ в значительной степени как дан выше: проблема происходит от попытки создать объект, который комбинирует набор с несколькими итераторами в тот набор. Решить ту проблему и целый бизнес с вперед и инвертировать итераторы, становится спорным.
Поскольку std :: deque
является контейнером произвольного доступа (то же самое, что std :: vector
) гораздо лучше использовать один целочисленный индекс в двухсторонней очереди для обоих обходов.
Может, тебе стоит переосмыслить свой выбор контейнера.
Обычно вам не нужно использовать реверсивные итераторы, чтобы идти назад,
currentfwd--
будет идти назад, хотя это может не сработать (что, я полагаю, вы и пытались сделать) с помощью dequeue.
Что вам действительно следует сделать, так это смоделировать ваш класс здесь в качестве декоратора dequeue и реализовать свои собственные Action-итераторы. Это было бы то, что я бы сделал в любом случае.
Я никогда не был в восторге от этого решения. Для одного, это не обязательно очевидно, где все такие методы в коде, для другого, я просто не люблю синтаксис. Я хочу использовать. когда я вызываю методы!
И я хочу использовать $! --- &
при вызове методов! Разберись. Если вы собираетесь написать код 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
или что-то подобное и поместите их туда.
Изменение ответа Фредди при использовании 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();
}
Я бы предпочел вернуть ссылку, хотя, и инкапсулировать все итерационные детали внутри класса.
Мне кажется, что у вас на самом деле есть два разных поведения в одном классе.
Примечательно, кажется, что вы можете пройти только вашу коллекцию в одном порядке, в противном случае, если вы должны были начать обход, а затем изменить аргумент
, вы получите довольно странную ситуацию.
Лично я все для разоблачения обеих итераторов (т. Е. Вперед начинается, конец, 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;
};
или если вы хотите динамически выбрать между передней и обратной траверсом, вы можете сделать итератор чистый виртуальный интерфейс и Имея вперед, так и назад.
Но на самом деле, я на самом деле не вижу смысла хранения как переадресации, так и назад и обратного итератора, если окажется, что вы будете использовать только один: /