Я пишу класс 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 …
Итак, вот мои вопросы:
reverse_iterator
из моего индивидуального класса iterator
чтобы он наследовал как можно больше функций?reverse_iterator
не повторяя себя полностью?Любая помощь очень ценится!
К сожалению, я не получил ответ, так что вот что я сделал. Может быть, уродливое решение побуждает кого-то опубликовать что-то приятное
Итак, первое, что я узнал, это то, что 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» вручную:
base()
,И с этим мы почти закончили. Почти из-за ...
в коде выше. Осталось делегировать все остальные вызовы таким функциям, как 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>;
и быть счастливым.
Увидеть Вот для полного кода.
Я все еще хотел бы видеть решение, которое избегает повторения большей части функциональности, но это достаточно хорошо для меня. И так как я не нашел ничего лучшего за долгое время, я решил поделиться этим.