A pretty minimal range type:
template<class It>
struct range_t {
private:
It b, e;
public:
It begin() const { return b; }
It end() const { return e; }
decltype(auto) front() const { return *b; }
decltype(auto) back() const { return *std::prev(e); }
bool empty() const { return b==e; }
range_t without_front( std::size_t n = 1 ) const {
auto r = *this;
std::advance(r.b,n);
return r;
}
range_t without_back( std::size_t n = 1 ) const {
auto r = *this;
std::advance(r.e,std::ptrdiff_t(-n));
return r;
}
range_t(It s, It f):b(std::move(s)), e(std::move(f)) {}
range_t():b(), e() {}
};
template<class It>
range_t<It> range( It b, It e ) {
return {std::move(b), std::move(e)};
}
Doing this task is far easier with ranges than with iterators.
template<class Outer, class Inner>
struct stacked_range_t {
range_t<Outer> outer;
stacked_range_t()=default;
stacked_range_t( range_t<Outer> o ):outer(std::move(o)) {}
struct iterator {
private:
range_t<Outer> outer;
range_t<Inner> inner;
public:
iterator(
range_t<Outer> o,
range_t<Inner> i
):outer(std::move(o)), inner(std::move(i)) {}
iterator()=default;
friend auto mytie(iterator const& it) {
return std::tie( it.outer.begin(), it.inner.begin(), it.inner.end() );
}
friend bool operator==(iterator const& lhs, iterator const& rhs) {
return mytie(lhs)==mytie(rhs);
}
friend bool operator!=(iterator const& lhs, iterator const& rhs) {
return mytie(lhs)==mytie(rhs);
}
using difference_type = std::ptrdiff_t;
using value_type = typename std::iterator_traits<Inner>::value_type;
using pointer = typename std::iterator_traits<Inner>::pointer;
using reference = typename std::iterator_traits<Inner>::reference;
using iterator_category = std::input_iterator_tag;
reference operator*() const {
return *inner.begin();
}
pointer operator->() const {
return inner.begin().operator->();
}
iterator& operator++() {
using std::begin; using std::end;
inner = inner.without_front();
while (inner.empty())
{
outer = outer.without_front();
if (!outer.empty())
inner = range( begin(outer.front()), end(outer.front()) );
}
return *this;
}
iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
};
iterator end() const {
return { range( outer.end(), outer.end() ), {} };
}
// a bit tricky:
iterator begin() const {
if (outer.empty()) return end();
auto rout = outer;
while( !rout.empty() ) {
using std::begin; using std::end;
auto rin = range( begin(rout.front()), end(rout.front()) );
if (!rin.empty())
return {std::move(rout), std::move(rin)};
rout = rout.without_front();
}
return end();
}
};
and a function to create it:
template<class Range>
auto make_stacked_range(Range& r) {
using std::begin; using std::end;
using Outer = decltype(begin(r));
using Inner = decltype(begin(*begin(r));
return stacked_range_t<Outer, Inner>{
{begin(r), end(r)}
};
}
there probably are typos. Use of C++1z features can be worked around with overly annoying decltype expressions and helper traits classes.
Relies on the iterators being trivially constructible, and such trivially constructed iterators are equal.