Created on 2025-11-23.00:00:00 last changed 2 weeks ago
Proposed resolution:
This wording is relative to N5014.
Modify [range.adjacent.view], class template `adjacent_view` synopsis, as indicated:
namespace std::ranges {
template<forward_range V, size_t N>
requires view<V> && (N > 0)
class adjacent_view : public view_interface<adjacent_view<V, N>> {
[…]
public:
[…]
constexpr auto begin() requires (!simple-view<V>) {
return iterator<false>(ranges::begin(base_), ranges::end(base_));
}
constexpr auto begin() const requires forward_range<const V> {
return iterator<true>(ranges::begin(base_), ranges::end(base_));
}
constexpr auto end() requires (!simple-view<V>) {
if constexpr (common_range<V>) {
return iterator<false>(as-sentinel{}, ranges::begin(base_), ranges::end(base_));
} else {
return sentinel<false>(ranges::end(base_));
}
}
constexpr auto end() const requires forward_range<const V> {
if constexpr (common_range<const V>) {
return iterator<true>(as-sentinel{}, ranges::begin(base_), ranges::end(base_));
} else {
return sentinel<true>(ranges::end(base_));
}
}
[…]
};
}
Currently `adjacent_view::begin` and `end const` overloads are constrained as follows
constexpr auto begin() const requires range<const V> constexpr auto end() const requires range<const V>
However, `adjacent_view` itself requires `forward_range`:
template<forward_range V, size_t N> requires view<V> && (N > 0) class adjacent_view;
This means that if the underlying range's `const begin/end` returns input-only iterator, the `adjacent_view`'s `const begin/end` are invocable, but they will result in some hard error (demo):
#include <ranges>
struct input_iter
{
const int* iter;
input_iter(const int* ii) : iter(ii) {}
input_iter(const input_iter&) = delete;
input_iter(input_iter&&) = default;
input_iter& operator=(input_iter const&) = delete;
input_iter& operator=(input_iter&&) = default;
using iterator_concept = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = int;
const int& operator*() const { return *iter; }
input_iter& operator++() { ++iter; return *this; }
void operator++(int) { ++iter; }
};
struct sent
{
const int* iter;
friend bool operator==(const input_iter& i, const sent& s) {
return i.iter == s.iter;
}
};
static_assert(std::input_iterator<input_iter>);
static_assert(!std::forward_iterator<input_iter>);
static_assert(std::sentinel_for<sent, input_iter>);
struct r : std::ranges::view_base
{
int* iter;
size_t size;
template<std::size_t N>
r(int (&i)[N]) : iter(i), size(N){}
auto begin() { return iter; }
auto end() { return iter + size; }
auto begin() const { return input_iter{iter}; }
auto end() const { return sent{iter + size}; }
};
static_assert(std::ranges::range<r>);
static_assert(std::ranges::range<const r>);
int main() {
int input[] = { 1, 2, 3, 4, 5 };
auto v = r{input} | std::views::adjacent<2>;
for (auto&& t : v) {} // ok
auto it = std::as_const(v).begin(); // ill-formed
}
Daniel:
This issue has considerable wording overlap with LWG 3731.| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2025-11-26 19:34:35 | admin | set | messages: + msg15765 |
| 2025-11-23 00:00:00 | admin | create | |