Code godbolt
template <typename T>
struct lattice_iterator {
public:
using difference_type = std::ptrdiff_t;
using value_type = std::vector<T>;
using iterator_category = std::input_iterator_tag;
private:
struct wrapped_iter {
T value_;
std::move_only_function<T(void)> adder_;
wrapped_iter(const wrapped_iter&) = delete;
wrapped_iter(wrapped_iter&&) = default;
wrapped_iter(std::ranges::input_range auto&& range) {
auto iter = range.begin();
value_ = *iter;
adder_ = [iter_ = std::move(iter)]() mutable { return *++iter_; };
}
bool operator<(wrapped_iter const& rhs) const {
return value_ < rhs.value_;
}
wrapped_iter& operator++() {
value_ = adder_();
return *this;
}
T& operator*() { return value_; }
};
public:
std::vector<wrapped_iter> iters_{};
value_type values_{};
template <std::ranges::input_range... R>
requires(std::convertible_to<std::ranges::range_value_t<R>, T> && ...)
lattice_iterator(R&&... ranges) {
(iters_.emplace_back(std::forward<R>(ranges)), ...);
values_ =
iters_ |
std::views::transform([](auto&& iter) { return iter.value_; }) |
std::ranges::to<std::vector>();
}
lattice_iterator(lattice_iterator const&) = delete;
lattice_iterator(lattice_iterator&&) = default;
lattice_iterator() = delete;
lattice_iterator& operator=(lattice_iterator&& rhs) = default;
value_type operator*() const { return values_; }
lattice_iterator& operator++() {
std::size_t min_idx = std::ranges::min(
std::views::iota(0) | std::views::take(iters_.size()), {},
[this](std::size_t idx) { return values_[idx]; });
values_[min_idx] = *++iters_[min_idx];
return *this;
}
void operator++(int) { ++*this; }
};
std::generator<int> get_triangles() {
for (int n = 1;; ++n) {
co_yield n*(n + 1) / 2;
}
}
std::generator<int> get_pentagonal() {
for (int n = 1;; ++n) {
co_yield n * (3 * n - 1) / 2;
}
}
std::generator<int> get_hexagonal() {
for (int n = 1;; ++n) {
co_yield n * (2 * n - 1);
}
}
int main() {
auto lattice = std::ranges::subrange(
lattice_iterator<int>{get_triangles(), get_pentagonal(),
get_hexagonal()},
std::unreachable_sentinel);
static_assert(std::same_as<std::vector<int>,
std::ranges::range_value_t<decltype(lattice)>>);
auto nums =
std::views::filter(
lattice,
[](auto&& pack) -> bool {
static_assert(std::same_as<std::vector<int>, decltype(pack)>);
auto p0 = pack.front();
return std::ranges::all_of(pack,
[&p0](auto px) { return px == p0; });
}) |
std::views::transform([](auto&& pack) { return pack.front(); }) |
std::views::take(3) | std::ranges::to<std::vector>();
std::cout << nums[1] << ' ' << nums[2] << '\n';
}
Context:
lattice_iterator
is a zip-alike wrapper that iterates by incrementing the smallest field amongst all current inputs
the code in main is for solving this. not really related to my question and forgive me its unnecessarily complicated for the problem. its for learning new C++ feature sake
Where I am stuck & how I got here
compiler is complaining that the subrange created is not a viewable range
search result suggests that the only non-viewable range is practically the ones that doesn't support copy constructor, from this post
So basically, the only thing that isn't a viewable range is an lvalue non-copyable view, because of the desire to avoid taking references to views.
input iterators, or essentially generator iterators are by definition only guarantees move movable but not copyable
However, generator as a viewable range, or at least std::views::filter compatible from my experience.
I suspect I made the wrong reasoning somewhere that led me into designing the subrange non-viewable while it should be viewable instead. Greatly appreciated if someone could point it out for me
An lvalue non-copyable view isn't a viewable-range, but an rvalue one is.
An easy fix is to std::move(lattice)
into the filter. See Godbolt
N.b. I also had to adjust the static_assert
in the lambda, and you have lifetime issues with your generators.