Унаследовать все функции от определенного пользователем итератора в reverse_iterator

Я пишу класс JSON для C ++ 11, см. http://github.com/nlohmann/json. Моя центральная структура данных — это класс, упаковывающий типы значений JSON (null, массив, объект, строка, bool, число) в объединение и предлагающий его через приятный интерфейс C ++. Как массивы (реализовано через std::vector) и объекты (std::map) приходят с их собственными итераторами, я реализовал итератор «обертка», который делегирует вызовы operator++ или же operator-> в соответствующие переменные-члены. Кроме того, я реализовал две дополнительные функции std::string key() для доступа к ключам объекта JSON и reference value() как псевдоним operator*(),

Пока все хорошо (см. https://github.com/nlohmann/json/blob/master/src/json.hpp для полного исходного кода) …

Тогда я хотел реализовать reverse_iterator а также const_reverse_iterator, И тут начинаются проблемы.

  • Если я понимаю их через using reverse_iterator = std::reverse_iterator<iterator>; а также using const_reverse_iterator = std::reverse_iterator<const_iterator>;, все хорошо, но функции key() а также value() не доступны с reverse_iterator или же const_reverse_iterator объекты.
  • Если я реализую свой собственный класс reverse_iterator лайк class reverse_iterator : public std::reverse_iterator<typename basic_json::iterator>Мне нужно снова реализовать весь класс. Недостаточно дать реализацию для key() а также value()но и для operator++() и все остальные вещи, которые я надеюсь получить бесплатно с помощью std::reverse_iterator адаптер.

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

Итак, вот мои вопросы:

  1. Как я могу создать reverse_iterator из моего индивидуального класса iterator чтобы он наследовал как можно больше функций?
  2. Если наследование поведения, выходящее за рамки стандартного, не работает автоматически, как я могу написать reverse_iterator не повторяя себя полностью?

Любая помощь очень ценится!

2

Решение

К сожалению, я не получил ответ, так что вот что я сделал. Может быть, уродливое решение побуждает кого-то опубликовать что-то приятное

Итак, первое, что я узнал, это то, что std::reverse_iterator ничего не делает, кроме как инкапсулировать «нормальный» итератор (называемый current, доступны через base()) и установить связь обратного итератора, являющегося «на один элемент больше слева», чем «нормальный» итератор.

отношения между итераторами и обратными итераторами

(изображение из cppreference.com)

Пока «обычный» итератор имеет стандартный интерфейс и не использует никаких дополнительных функций, строки

using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;

достаточно для автоматического преобразования вашего собственного класса итератора в обратный итератор.

И с функциями-членами

reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }

возможна обратная итерация, например, такой код

for (my_container::reverse_iterator rit = c.rbegin(); rit != c.rend(); ++it)
{
// rit will iterator container c in reverse order
}

работает.

Как я уже писал в вопросе, я продлил iterator класс с двумя дополнительными членами, key() а также value(), Первый обеспечивает быстрый доступ к ключу объекта JSON во время итерации. Последний псевдоним для записи it.value() скорее, чем *it, К сожалению, вышеуказанный подход не наследует эти функции reverse_iterator,

Чтобы обогатить определенные пользователем обратные итераторы, нам нужно наследовать от std::reverse_iterator<iterator> и делегировать вызовы базовому итератору. К сожалению, я не нашел другого подхода, кроме как сделать это вручную. Для упомянутых функций это выглядит следующим образом:

class reverse_iterator : public std::reverse_iterator<iterator>
{
...

std::string key() const
{
auto it = --this->base();
return it.key();
}

reference value() const
{
auto it = --this->base();
return it.operator * ();
}
}

Самым сложным является то, что вам нужно реализовать отношение «off-by-one» вручную:

  1. Получить базовый итератор через base(),
  2. Уменьшите его, чтобы он указывал на «правый» (на самом деле …) элемент.
  3. Вызовите нужную функцию.

И с этим мы почти закончили. Почти из-за ... в коде выше. Осталось делегировать все остальные вызовы таким функциям, как operator++ в базовый класс. Теперь я нашел способ пустить кого-то в эту скучную делегацию.

Таким образом, класс содержит такой код

using base_iterator = std::reverse_iterator<iterator>;

reverse_iterator operator++(int)
{
return base_iterator::operator++(1);
}

reverse_iterator& operator++()
{
base_iterator::operator++();
return *this;
}

(Обратите внимание на определение base_iterator.)

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

for (my_container::reverse_iterator rit = c.rbegin(); rit != c.rend(); ++it)
{
std::cout << rit.key() << '\n';
}

В дискуссии о Github, gregmarr предложил объединить reverse_iterator и const_reverse_iterator классы в один класс шаблона, как

template<typename Base>
class json_reverse_iterator : public std::reverse_iterator<Base>
{
public:
/// shortcut to the reverse iterator adaptor
using base_iterator = std::reverse_iterator<Base>;
/// the reference type for the pointed-to element
using reference = typename Base::reference;

/// create reverse iterator from iterator
json_reverse_iterator(const typename base_iterator::iterator_type& it)
: base_iterator(it) {}

/// create reverse iterator from base class
json_reverse_iterator(const base_iterator& it) : base_iterator(it) {}

...
}

Это позволяет писать

using reverse_iterator = json_reverse_iterator<iterator>;
using const_reverse_iterator = json_reverse_iterator<const_iterator>;

и быть счастливым.

Увидеть Вот для полного кода.

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

0

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


По вопросам рекламы [email protected]