Как уже отмечалось в комментариях, входные данные могут быть лучше определены, чтобы соответствовать их значению, и это позволит избежать этой проблемы наилучшим образом.
Но если вы по какой-то причине застряли в последовательности, где N элементов время от времени что-то значит, и вам нужно много с этим работать, вот как я бы использовал boost::iterator_adaptor
для создания итератора, который одновременно захватывает N элементов.
#include <cstddef>
#include <utility>
#include <iterator>
#include <tuple>
#include <boost/iterator/iterator_adaptor.hpp>
namespace N_elements_iterator_detail {
template <typename T, std::size_t N>
using ignore_N = T;
template <typename T, typename IndSeq>
struct repeat_type_helper;
template <typename T, std::size_t ...Inds>
struct repeat_type_helper<T, std::index_sequence<Inds...>>
{ using type = std::tuple<ignore_N<T, Inds>...>; };
template <typename T, std::size_t N>
using repeat_type = typename repeat_type_helper<T, std::make_index_sequence<N>>::type;
template <typename IndSeq>
struct deref_helper;
template <std::size_t ...Inds>
struct deref_helper<std::index_sequence<Inds...>>
{
template <typename RetType, typename FwdIter>
static RetType deref(FwdIter iter) {
FwdIter iter_array[] =
{ (static_cast<void>(Inds), iter++) ... };
return RetType( *iter_array[Inds]... );
}
};
}
template <typename FwdIter,
typename std::iterator_traits<FwdIter>::difference_type N,
std::enable_if_t<(N>0)>* = nullptr>
class N_elements_iterator :
public boost::iterator_adaptor<
N_elements_iterator<FwdIter, N>, // CRTP Derived type
FwdIter, // Implementation base iterator
N_elements_iterator_detail::repeat_type<
typename std::iterator_traits<FwdIter>::value_type, N>,
boost::use_default, // Iterator category
N_elements_iterator_detail::repeat_type<
typename std::iterator_traits<FwdIter>::reference, N>>
{
public:
using N_elements_iterator::iterator_adaptor::iterator_adaptor;
private:
friend class boost::iterator_core_access;
typename N_elements_iterator::reference dereference() const {
return N_elements_iterator_detail::deref_helper<
std::make_index_sequence<N>>
::template deref<typename N_elements_iterator::reference>(
this->base());
}
void advance(typename N_elements_iterator::difference_type dist) {
std::advance(this->base_reference(), N * dist);
}
void increment() { advance(1); }
void decrement() { advance(-1); }
// N must be the same, but we can subtract for example
// N_elements_iterator<C::iterator, N> and N_elements_iterator<C::const_iterator, N>
template <typename OtherIter>
auto distance_to(const N_elements_iterator<OtherIter, N>& other) const {
return N * std::distance(this->base(), other.base());
}
};
template <auto N, typename FwdIter>
N_elements_iterator<FwdIter, N> make_N_elements_iterator(FwdIter iter)
{ return N_elements_iterator<FwdIter, N>{ iter }; }
// -----
#include <vector>
#include <iostream>
int main() {
std::vector rgb_colors = {1,1,1,2,2,2,3,3,3,4,4,4};
for (auto c = make_N_elements_iterator<3>(rgb_colors.cbegin());
c != make_N_elements_iterator<3>(rgb_colors.cend());
++c) {
auto&& [red, green, blue] = *c;
std::cout << "red " << red
<< ", green " << green
<< ", blue " << blue
<< '\n';
}
}
См. эта программа на колиру .