приведение возвращенного итератора к const - PullRequest
0 голосов
/ 29 августа 2018

В моем коде есть следующее выражение for:

for (auto Iter = Target.begin(), 
        IterEnd = std::stable_partition(Target.begin(), Target.end(), Check);
    Iter != IterEnd; ++Iter)
    { /* loop statement */ }

Дело в том, что цикл не изменяет элементы контейнера, поэтому имеет смысл объявить итераторы как const_iterator. Я легко могу решить проблему для первого, используя cbegin(), но второе более сложное. Я не могу объявить cbegin() и cend() внутри stable_partition, поскольку, конечно, stable_partition требуется non const_iterators для своей работы.

Возможное решение - заменить auto на соответствующий тип, который в данном случае был std::vector< std::string >::const_iterator. Это вызывает преобразование из iterator в const_iterator во втором назначении.

Хотя мне это не нравится. Типы могут легко и быстро стать неуправляемыми, поэтому я ищу решение, которое позволило бы мне использовать auto без необходимости объявлять что-то странное вне цикла. Любое предложение?

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Вот один из способов выразить идею через функциональный интерфейс:

#include <vector>
#include <algorithm>
#include <iostream>

namespace detail {
    template<class Container, class F>
    struct const_partitioned_target
    {
        using container_type = std::decay_t<Container>;
        using const_iterator = typename container_type::const_iterator;

        const_partitioned_target(Container& cont, F f)
            : first(cont.cbegin())
            , last(std::partition(cont.begin(), cont.end(), f))
        {

        }

        const_iterator begin() const { return first; }

        const_iterator end() const { return last; }

        const_iterator first, last;
    };
}

template<class Container, class F>
auto const_partitioned_target(Container& cont, F&& f)
{
    return detail::const_partitioned_target<Container, std::decay_t<F>>(cont, std::forward<F>(f));
};

int main()
{
    std::vector<int> Target { 1, 2, 6, 9, 10, 20, 30, 40 };

    auto Check = [](auto&& x)
    {
        return x < 10;
    };

    for(auto&& elem : const_partitioned_target(Target, Check))
    {
        // elem will have the type: int const&
        std::cout << elem << '\n';
    }
}

ожидаемый результат:

1
2
6
9
0 голосов
/ 29 августа 2018

Самое ясное решение, на мой взгляд, это потянуть std::stable_partition до for. Это приведет к эквивалентному алгоритму.

Проблема в том, что stable_partition возвращает итератор, который может изменять элементы. К счастью, существует неявное преобразование из container::iterator в container::const_iterator (для большинства стандартных контейнеров). Чтобы выполнить преобразование, вы можете указать тип IterEnd с std::vector<T::const_iterator или decltyp(Target.cbegin() или мои личные предпочтения:

auto Iter = Target.cbegin();
decltype(Iter) IterEnd = std::stable_partition(Target.begin(), Target.end(), Check);

for (; Iter != IterEnd; ++Iter)
{
}

Для полноты вы можете оставить все внутри for, если хотите, но, на мой взгляд, он менее читабелен:

for (auto Iter = Target.cbegin(),
        IterEnd = (decltype(Iter)) std::stable_partition(Target.begin(), Target.end(), Check);
     Iter != IterEnd;
     ++Iter)
{}
...