Должен ли обращение обратного итератора давать прямой итератор исходного типа? - PullRequest
0 голосов
/ 20 сентября 2018

Я наивно ожидал, что эта программа будет скомпилирована и запущена со статусом успеха:

#include <iterator>
#include <string>

int main()
{
    const std::string s = "foo";
    auto forward_iter = s.begin();
    auto reverse_iter = std::make_reverse_iterator(forward_iter);
    auto third_iter = std::make_reverse_iterator(reverse_iter);
    return forward_iter != third_iter;
}

Не удается скомпилировать, потому что тип third_iter отличается от типа forward_iterмы начали с;вместо этого это reverse_iterator<reverse_iterator<normal_iterator>>:

0.cpp:10:25: error: no match for ‘operator!=’ (operand types are ‘__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >’ and ‘std::reverse_iterator<std::reverse_iterator<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > > >’)
     return forward_iter != third_iter;
            ~~~~~~~~~~~~~^~~~~~~~~~~~~

Перечитывая документацию, создается впечатление, что std::make_reverse_iterator(it) указано для всегда переноса it, , даже если it уже обратный итератор (и в некоторой степени это имеет смысл, поскольку мы ожидаем использовать reverse_iterator членов (т. Е. base()).

Существует ли стандартный способ переключения между обычным (вперед) иобернутые (обратные) итераторы, не зная, какой у меня тип? Или мне нужно написать пару функций SFINAE для возврата std::make_reverse_iterator(it) и it.base() соответственно?

Ответы [ 2 ]

0 голосов
/ 20 сентября 2018

Мне удалось создать пару небольших функций для переключения между прямым и обратным итераторами:

template<typename Iter>
auto toggle_iterator_direction(Iter it) {
    return std::make_reverse_iterator(it);
}

template<typename Iter>
auto toggle_iterator_direction(std::reverse_iterator<Iter> it) {
    return it.base();
}

И альтернативная версия, использующая шаблон вспомогательного типа, смоделированная на ответ на Определите, является ли итератор (c ++) обратным :

template<typename Iter>
struct is_reverse_iterator : std::false_type {};

template<typename Iter>
struct is_reverse_iterator<std::reverse_iterator<Iter>> : std::true_type {};


template<typename Iter>
auto toggle_iterator_direction(Iter it) {
    if constexpr (is_reverse_iterator<Iter>())
        return it.base();
    else
        return std::make_reverse_iterator(it);
}

Оба эти исправления модифицируют тестовую программу:

#include <iterator>
#include <string>

int main()
{
    const std::string s = "foo";
    auto forward_iter = s.begin();
    auto reverse_iter = toggle_iterator_direction(forward_iter);
    auto third_iter = toggle_iterator_direction(reverse_iter);
    return forward_iter != third_iter;
}
0 голосов
/ 20 сентября 2018

Должен ли обратный итератор давать обратный итератор исходного типа?

Нет.Или, по крайней мере, это не так, как было указано make_reverse_iterator.В стандарте тип возвращаемого значения reverse_iterator<Iterator>.

Существует ли стандартный способ обмена между обычными (прямыми) и переносимыми (обратными) итераторами, не зная, какой у меня тип?

Нет.Не так далеко, насколько я знаю.

Или мне нужно написать пару функций SFINAE для возврата std::make_reverse_iterator(it) и it.base() соответственно?

Вы можете написатьЭто.Я не могу сказать, нужно ли вам это.

Вот одна реализация:

#include <iterator>
#include <type_traits>

// https://stackoverflow.com/a/35408829/2079303
template<typename I>
struct is_reverse_iterator : std::false_type {};

template<typename I>
struct is_reverse_iterator<std::reverse_iterator<I>>
: std::integral_constant<bool, !is_reverse_iterator<I>::value> {};

template<class It>
auto
reverse_or_base(It&& i)
{
    if constexpr (is_reverse_iterator<std::decay_t<It>>())
        return i.base();
    else
        return std::make_reverse_iterator(std::forward<It>(i));
}

И тест:

#include <vector>
#include <cassert>
int main() {
    std::vector<int> v;
    static_assert(
        std::is_same_v<
            decltype(reverse_or_base(v.rbegin())),
            std::vector<int>::iterator
        >
    );
    assert(v.end() == reverse_or_base(v.rbegin()));

    static_assert(
        std::is_same_v<
            decltype(reverse_or_base(v.begin())),
            std::vector<int>::reverse_iterator
        >
    );
    assert(v.rend() == reverse_or_base(v.begin()));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...