user14015333
user14015333

Reputation: 45

Iterate over a container with a custom start/end position

I'd like to iterate over a container (say a std::vector) but not from the beginning. I am basically trying to replicate boost::make_iterator_range(v.begin() + 1, v.end()).

I came up with this:

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

int main()
{
    std::vector<int> v {1, 2, 3};
    
    std::for_each_n(v.begin() + 1, v.size() - 1, [](auto& n) 
    {
        std::cout << n << '\n';
    });
}

However this seems like a poor solution. Also it requires C++17 while I am looking for a solution that works in C++14.
Is there a better way to achieve this without the use of third party libraries?

Upvotes: 0

Views: 127

Answers (3)

eerorika
eerorika

Reputation: 238301

Is there a better way to achieve this ...

A solution using ranges (courtesy of cigien in comments):

for (int element : v | ranges::drop_view(1))

A solution using span

for (int element : span(&v[1], v.size() - 1))

Of course, there aren't std::ranges nor std::span until C++20, so unless you want to use a third party implementation, you'll need to write your own.


If you don't want to implement your own libraries, nor can use C++20, nor want to use a third party library, then std::for_each an alternative that is in C++11.

Upvotes: 2

super
super

Reputation: 12928

To replicate make_iterator_range you need an object with a begin and end function. Something like this.

template <typename T>
struct iterator_range {
    iterator_range(T begin, T end) : m_begin(begin), m_end(end) {}

    T begin() {
        return m_begin;
    }

    T end() {
        return m_end;
    }

    T m_begin, m_end;
};

Then we can make our make_iterator_range function that return an iterator_range.

#include <vector>
#include <iostream>

template <typename T>
struct iterator_range {
    iterator_range(T begin, T end) : m_begin(begin), m_end(end) {}

    T begin() {
        return m_begin;
    }

    T end() {
        return m_end;
    }

    T m_begin, m_end;
};

template <typename T>
auto make_iterator_range(T begin, T end) {
    return iterator_range<T>(begin, end);
}

int main()
{
    std::vector<int> v {1, 2, 3};

    auto range = iterator_range<decltype(v.begin())>(v.begin() + 1, v.end());
    
    for (auto& i : range) {
        std::cout << i << '\n';
    }

    for (auto& i : make_iterator_range(v.begin() + 1, v.end())) {
        std::cout << i << '\n';
    }
}

Upvotes: 2

n314159
n314159

Reputation: 5075

std::for_each is in C++11 and with that you can do:

std::for_each(v.begin() + 1, v.end(), [](auto &n) { ... });

Upvotes: 2

Related Questions