Возврат коллекции через итераторы из шаблона - PullRequest
0 голосов
/ 08 ноября 2018

Допустим (для простоты), что я хочу передать коллекцию методу, метод применит некоторые func к каждому элементу коллекции и вернет эту коллекцию. Например. в C # это будет выглядеть так

IEnumerable<Tout> Transform<Tin, Tout>(IEnumerable<Tin> collection, 
    Func<Tin, Tout> func)
{
    return collection.Select(x => func(x));
}

Моя цель - написать эквивалентную функцию на C ++. Согласно этому вопросу мне нужно передать в функцию два итератора, которые будут представлять границы входной коллекции. Это решает проблему прохождения сбора. Но как вернуть коллекцию?

Я думал, что мне следует применить ту же логику и вернуть пару итераторов из функции, которая будет представлять возвращаемую коллекцию.

Вот как я пытался написать эквивалентную функцию:

template<typename ForwardIterator, typename ReturnIterator>
std::pair<ReturnIterator, ReturnIterator> Transform(
    ForwardIterator begin, ForwardIterator end,
    std::function <
        typename std::iterator_traits<ReturnIterator>::value_type      
       (typename std::iterator_traits<ForwardIterator>::value_type) 
    > func)
{
    using InputType = std::iterator_traits<ForwardIterator>::value_type;
    using RetType = std::iterator_traits<ReturnIterator>::value_type;   
    std::vector<RetType> ans;
    std::transform(begin, end, std::back_inserter(ans),
        [&](InputType el) -> RetType { return func(el); } );    
    return { std::begin(ans), std::end(ans) };
}

int main()
{
    // Simple example -> converts every int to string from inputCollection
    std::vector<int> inputCollection = { 1,2,3 };
    auto retCollecction = Transform<std::vector<int>::iterator, std::vector<std::string>::iterator>
        (std::begin(inputCollection),
            std::end(inputCollection),
            [](int el)-> std::string {return std::to_string(el); });
}

Очевидно, что это нехорошо, поскольку выходной набор удаляется, как только я выхожу из функции, и итераторы ничего не указывают. Итак, как это исправить, и Какой должен быть лучший способ написать это на C ++ .

Примечание: я не хочу передавать и возвращать vector<T> или какую-либо другую конкретную коллекцию. Я хотел бы общий подход, который может иметь дело с любым типом коллекций.

Ответы [ 3 ]

0 голосов
/ 08 ноября 2018

Библиотека ranges использует концепцию диапазонов - пара начальных и конечных итераторов. Если вы планируете много писать LINQ-подобный код, вы, вероятно, должны изучить его и основывать свой код на его концепциях:

https://github.com/ericniebler/range-v3

0 голосов
/ 08 ноября 2018

(текущая стандартная библиотека) C ++ - это принять выходной итератор , см. std::transform.

(будущий) способ C ++ - вернутьRange значение, см. ranges::transform

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

template<typename InputIterator, typename Func>
auto transform(InputIterator begin, InputIterator end, Func func)
{
    using coro_t = boost::coroutines2::coroutine<decltype(func(*begin))>;
    return coro_t::pull_type([=](coro_t::push_type& yield)
    {
        std::transform(begin, end, yield, func);
    });
}
0 голосов
/ 08 ноября 2018

Общий подход C ++ - принять итератор output . Пусть вызывающая сторона решит, куда должен идти вывод.

...