Давайте предположим, что я хочу написать двунаправленный итератор, который перебирает все ненулевые значения любого контейнера, предоставляя begin()
/ end()
/ rbegin()
/ rend()
. Мне пришлось бы переписать operator++()
, чтобы пропустить все нули, с которыми он сталкивается. Чтобы убедиться, что он все еще действителен, он должен будет каждый раз проверять end()
и rend()
контейнера. Что-то в строках следующего:
template<class Container, class Iter>
struct NonZeroIter: public Iter
{
Container& c;
using Parent = Iter;
using Parent::Parent;
using iterator_category = std::bidirectional_iterator_tag;
bool is_valid() const { return *(*this) != 0; }
bool is_end() const { return *this == c.end(); }
bool is_rend() const { return *this == c.rend(); }
NonZeroIter(Container& _c, const Iter& _it):
Parent(_it),
c(_c)
{ if(!is_end() && !is_valid()) ++(*this); }
NonZeroIter& operator++()
{
if(!is_end()){
do{
Parent::operator++();
} while(!is_end() && !is_valid());
}
return *this;
}
NonZeroIter& operator--()
{
if(!is_rend()){
do{
Parent::operator--();
} while(!is_rend() && !is_valid());
}
return *this;
}
NonZeroIter& operator++(int) { NonZeroIter tmp(*this); ++(*this); return tmp; }
NonZeroIter& operator--(int) { NonZeroIter tmp(*this); --(*this); return tmp; }
};
Теперь я хочу сделать обратный итератор для NonZeroIter
, используя std::reverse_iterator
, но для этого мне придется проверять rend()
каждый раз, когда NonZeroIter
проверяет end()
и наоборот. Есть ли хороший способ (по возможности, избежать накладных расходов) сделать это или мне нужно написать свой собственный соответствующий класс обратного итератора?
2 ответа
Одним из возможных решений может быть базовый класс итераторов, где is_end
и is_rend
являются виртуальными абстрактными функциями.
Затем создайте классы прямого и обратного итераторов, унаследованные от базового класса, и реализующие функции is_end
и is_rend
, необходимые для каждого типа итераторов.
Вместо NonZeroIter
явной проверки по end()
и rend()
, конструктор должен проверить направление итерации и выбрать начало (begin()
или rbegin()
) и конец ({ {X5}} или rend()
). Их можно сохранить как локальные переменные и проверить.
Вместо проверки на «rend» в operator--()
вы можете проверить на «begin» (индекс, представленный begin()
, совпадает с rend() - 1
).
Все итераторы стандартных контейнеров основаны на std::reverse_iterator
, поэтому вы можете использовать эти знания, чтобы найти направление _it
.
Что-то вроде этого:
template<typename T>
struct is_reverse_iterator : std::false_type {};
template<typename T>
struct is_reverse_iterator<std::reverse_iterator<T>> : std::true_type {};
template<class Container, class Iter>
struct NonZeroIter: public Iter
{
using Parent = Iter;
using Parent::Parent;
using iterator_category = std::bidirectional_iterator_tag;
private:
Parent begin, end;
bool is_valid() const { return *(*this) != 0; }
bool is_end() const { return *this == end; }
bool is_begin() const { return *this == begin; }
public:
NonZeroIter(Container& c, const Iter& _it):
Parent(_it),
begin(is_reverse_iterator<Parent> ? c.rbegin() : c.begin()),
end(is_reverse_iterator<Parent> ? c.rend() : c.end()),
{ if (!is_end() && !is_valid()) ++(*this); }
NonZeroIter& operator++()
{
if (!is_end()){
do{
Parent::operator++();
} while(!is_end() && !is_valid());
}
return *this;
}
NonZeroIter& operator--()
{
// Smallest possible value is begin, but you could also make that begin - 1
if (!is_begin()){
do{
Parent::operator--();
} while(!is_begin() && !is_valid());
}
return *this;
}
NonZeroIter& operator++(int) { NonZeroIter tmp(*this); ++(*this); return tmp; }
NonZeroIter& operator--(int) { NonZeroIter tmp(*this); --(*this); return tmp; }
};
Похожие вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .