Как конвертировать итераторы разных типов - PullRequest
4 голосов
/ 30 октября 2019

У меня есть устаревшая функция Foo, которая принимает два итератора в качестве входных данных, и я хочу использовать ее повторно, но не должен менять ее интерфейс (хотя я могу изменить тип итераторов, но не могу сделать это шаблоном)
проблема в том, что у меня есть итераторы другого типа, и мне нужно скопировать исходный контейнер, чтобы получить итераторы, необходимые для Foo функции
Мне было интересно, есть ли способ преобразовать итераторы одного типа в итераторы другого типа?
Iпытался использовать boost::make_transform_iterator, но он не скомпилируется, говоря, что итераторы бывают разных типов
Можно ли раскомментировать и использовать option 2 (см. код ниже)?

#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>

#include <boost/iterator/transform_iterator.hpp>

/* Method with fixed interface */
void Foo(std::vector<int>::const_iterator beginIt, std::vector<int>::const_iterator endIt)
{
    std::cout << "ints:" << std::endl;
    std::copy(beginIt, endIt, std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
}

int main()
{
    const std::vector<std::string> strings { "2", "4", "6" };
    auto convertStringToInt = [](const std::string & s) { return std::stoi(s); };

    // 1) Works, but creates another container with size of initial container
    std::vector<int> convertedInts;
    std::transform(strings.begin(), strings.end(), std::back_inserter(convertedInts), convertStringToInt);
    Foo(convertedInts.begin(), convertedInts.end());

    // 2) Doesn't compile, but operates only with iterators hence there's minimum overhead
    //auto beg = boost::make_transform_iterator(strings.begin(), convertStringToInt);
    //auto end = boost::make_transform_iterator(strings.end(), convertStringToInt);
    //Foo(beg, end);

    std::cout << "strings:" << std::endl;
    std::copy(strings.begin(), strings.end(), std::ostream_iterator<std::string>(std::cout, " "));
}

Код можетбыть скомпилированным в онлайн-компиляторе wandbox (у него нет функции «поделиться»)

Редактировать: Foo реализует логику, строго специфичную для intТак что сделать его непатентованным невозможно. Но у меня есть контейнер (из std::string как в примере), элементы которого я могу преобразовать в целые с помощью лямбды без захвата.
Кажется очень странным, что не существует стандартного способа обернуть один итератор в другой :)

Ответы [ 3 ]

1 голос
/ 31 октября 2019

Вы можете стереть ввод, например, используя boost::any_range<int> (или это iterator тип).

1 голос
/ 30 октября 2019

Вы можете создать шаблон:

template<typename some_iterator>
void Foo(some_iterator beginIt, some_iterator endIt)
{
    ...
}

Или более строгую версию

template<typename some_container>
void Foo(typename some_container::const_iterator beginIt,
         typename some_container::const_iterator endIt)
{
    ...
}

Если вы используете C ++ 11, вы можете создать еще более строгую версию, используя static_assert

template<typename some_container>
void Foo(typename some_container::const_iterator beginIt,
         typename some_container::const_iterator endIt) 
{
    static_assert(std::is_same<typename some_container::value_type, int>,
        "Only integer containers are accepted");
    ...
}

Или используя enable_if

template<typename some_container>
auto Foo(typename some_container::const_iterator beginIt,
         typename some_container::const_iterator endIt) ->
             typename std::enable_if<std::is_same<
             typename some_container::value_type,int>, void>::type
{
    ...
}
0 голосов
/ 30 октября 2019

Это невозможно. vector::const_iterator - это просто прославленный необработанный указатель, и нет способа подключить какую-либо функциональность к разыменованию необработанного указателя.

Вам придется принять издержки, если вы не можете изменить сигнатуру функции.

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