template <std::ranges::view TBase> requires std::ranges::forward_range<TBase>
class all_pairs_view :
public std::ranges::view_interface<all_pairs_view<TBase>> // CRTP!
{
private:
TBase _vw;
public:
class iterator { /* ... */ };
all_pairs_view() = default;
constexpr all_pairs_view(TBase vw);
[[nodiscard]] constexpr iterator begin() const;
[[nodiscard]] constexpr iterator end() const;
};
CLICK [Describe inputs and template types]
A view is usually just a supplier
of a fancy iterator.
of a fancy iterator.
template <std::ranges::view TBase> requires std::ranges::forward_range<TBase>
class all_pairs_view :
public std::ranges::view_interface<all_pairs_view<TBase>>
{
private:
TBase _vw;
class inner_iterator { /* ... */ };
class inner_sentinel { /* ... */ };
class inner_view { /* ... */ };
class outer_iterator { /* ... */ };
public:
using iterator = outer_iterator;
all_pairs_view() = default;
constexpr all_pairs_view(TBase vw);
[[nodiscard]] constexpr iterator begin() const;
[[nodiscard]] constexpr iterator end() const;
};
template <std::ranges::view TBase> requires std::ranges::forward_range<TBase>
class all_pairs_view :
public std::ranges::view_interface<all_pairs_view<TBase>>
{
private:
TBase _vw;
public:
constexpr all_pairs_view(TBase vw) : _vw{ std::move(vw) } { }
};
template <class Rng>
all_pairs_view(Rng&&) -> all_pairs_view<std::views::all_t<Rng>>;
template <std::ranges::view TBase> requires std::ranges::forward_range<TBase>
class all_pairs_view :
public std::ranges::view_interface<all_pairs_view<TBase>>
{
private:
TBase _vw;
class inner_iterator { /* ... */ };
class inner_sentinel { /* ... */ };
class inner_view { /* ... */ };
class outer_iterator { /* ... */ };
public:
using iterator = outer_iterator;
all_pairs_view() = default;
constexpr all_pairs_view(TBase vw);
[[nodiscard]] constexpr iterator begin() const;
[[nodiscard]] constexpr iterator end() const;
};
class inner_iterator
{
private:
friend class inner_sentinel;
using base_iterator = std::ranges::iterator_t<TBase>;
using base_sentinel = std::ranges::sentinel_t<TBase>;
using base_value_type = std::ranges::range_value_t<TBase>;
using base_reference = std::ranges::range_reference_t<TBase>;
/* ... */
public:
using value_type = std::pair<base_value_type, base_value_type>;
using reference = std::pair<base_reference, base_reference>;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
/* ... */
};
You may notice I don't define "pointer" here. Before C++20, we had to define 5 types, these 4 plus pointer, in order to be considered an iterator. Now, if pointer doesn't make sense for your iterator type, you don't actually have to define it.
class inner_iterator
{
private:
/* ... */
base_iterator _current_outer{};
base_iterator _current_inner{};
TBase const* _base{};
void correct_inner_if_needed();
public:
/* ... */
inner_iterator() = default;
inner_iterator(TBase const& base, base_iterator current_outer);
[[nodiscard]] reference operator*() const;
inner_iterator& operator++();
[[nodiscard]] inner_iterator operator++(int)
[[nodiscard]] bool operator==(inner_iterator const& rhs) const;
};
class inner_iterator
{
private:
friend class inner_sentinel;
using base_iterator = std::ranges::iterator_t<TBase>;
using base_sentinel = std::ranges::sentinel_t<TBase>;
using base_value_type = std::ranges::range_value_t<TBase>;
using base_reference = std::ranges::range_reference_t<TBase>;
base_iterator _current_outer{};
base_iterator _current_inner{};
TBase const* _base{};
void correct_inner_if_needed()
{
if (_current_inner == _current_outer)
{
std::advance(_current_inner, 1);
}
}
public:
using value_type = std::pair<base_value_type, base_value_type>;
using reference = std::pair<base_reference, base_reference>;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
inner_iterator() = default;
inner_iterator(TBase const& base, base_iterator current_outer)
: _current_outer{ std::move(current_outer) }
, _current_inner{ std::ranges::begin(base) }
, _base{ std::addressof(base) }
{
correct_inner_if_needed();
}
[[nodiscard]] reference operator*() const
{
return { *_current_outer, *_current_inner };
}
inner_iterator& operator++()
{
++_current_inner;
correct_inner_if_needed();
return *this;
}
[[nodiscard]] inner_iterator operator++(int)
{
auto tmp = *this;
++(*this);
return tmp;
}
[[nodiscard]] bool operator==(inner_iterator const& rhs) const
{
return _current_outer == rhs._current_outer &&
_current_inner == rhs._current_inner;
}
/* As of C++20, operator!= is auto-generated as !(operator==) */
};
template <std::ranges::view TBase> requires std::ranges::forward_range<TBase>
class all_pairs_view :
public std::ranges::view_interface<all_pairs_view<TBase>>
{
private:
TBase _vw;
class inner_iterator { /* ... */ };
class inner_sentinel { /* ... */ };
class inner_view { /* ... */ };
class outer_iterator { /* ... */ };
public:
using iterator = outer_iterator;
all_pairs_view() = default;
constexpr all_pairs_view(TBase vw);
[[nodiscard]] constexpr iterator begin() const;
[[nodiscard]] constexpr iterator end() const;
};