Alexey Andronov
Alexey Andronov

Reputation: 602

How to convert iterators of different types

I have a legacy function Foo which takes two iterators as input and I want to reuse it but should not change its interface (although I can change the type of iterators but can't make it template)
The problem is that I have iterators of different type and have to copy initial container to get iterators needed for Foo function
I was wondering is there any way to convert iterators of one type to iterators of another type?
I tried to use boost::make_transform_iterator but it won't compile saying iterators are of different types
Is it possible to uncomment and use the option 2 (see code below)?

#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, " "));
}

The code can be compiled in online-compiler wandbox (it doesn't have a "share" feature)

Edit: Foo implements logic strictly specific for int's so it's not possible to make it generic. But I have a container (of std::string's as in example) elements of which I can convert to ints via non-capturing lambda.
It seems very odd that there no standard way to wrap one iterator into another :)

Upvotes: 5

Views: 2187

Answers (3)

Caleth
Caleth

Reputation: 63152

You can type-erase the input, e.g. using boost::any_range<int> (or it's iterator type).

Upvotes: 1

Naomi
Naomi

Reputation: 5486

You can template it:

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

Or the more strict version

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

If you are using C++11 you can create an even more strict version using 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");
    ...
}

Or using 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
{
    ...
}

Upvotes: 3

Ayjay
Ayjay

Reputation: 3433

This isn't possible. vector::const_iterator is basically just a glorified raw pointer, and there's no way to hook any functionality into dereferencing a raw pointer.

You'll have to accept the overhead if you can't change the function signature.

Upvotes: 0

Related Questions