This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
adjacent_view::{begin,end} const overloads are under-constrainedSection: 25.7.27.2 [range.adjacent.view] Status: New Submitter: Hui Xie Opened: 2025-11-23 Last modified: 2025-11-26
Priority: Not Prioritized
View all other issues in [range.adjacent.view].
View all issues with New status.
Discussion:
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(i).Proposed resolution:
This wording is relative to N5014.
Modify 25.7.27.2 [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_));
}
}
[…]
};
}