Skip to content

Commit e44d873

Browse files
committed
New issue from Hui Xie: "adjacent_view::{begin,end} const overloads are under-constrained"
1 parent 2f89799 commit e44d873

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed

xml/issue4482.xml

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4482" status="New">
5+
<title>`adjacent_view::{begin,end}` const overloads are under-constrained</title>
6+
<section><sref ref="[range.adjacent.view]"/></section>
7+
<submitter>Hui Xie</submitter>
8+
<date>23 Nov 2025</date>
9+
<priority>99</priority>
10+
11+
<discussion>
12+
<p>
13+
Currently `adjacent_view::begin` and `end const` overloads are constrained as follows
14+
</p>
15+
<blockquote><pre>
16+
constexpr auto begin() const requires range&lt;const V&gt;
17+
constexpr auto end() const requires range&lt;const V&gt;
18+
</pre></blockquote>
19+
<p>
20+
However, `adjacent_view` itself requires `forward_range`:
21+
</p>
22+
<blockquote><pre>
23+
template&lt;forward_range V, size_t N&gt;
24+
requires view&lt;V&gt; &amp;&amp; (N &gt; 0)
25+
class adjacent_view;
26+
</pre></blockquote>
27+
<p>
28+
This means that if the underlying range's `const begin/end` returns input-only iterator,
29+
the `adjacent_view`'s `const begin/end` are invocable, but they will result in some hard error
30+
(<a href="https://godbolt.org/z/zs4aoPfjj">demo</a>):
31+
</p>
32+
<blockquote><pre>
33+
#include &lt;ranges&gt;
34+
35+
struct input_iter
36+
{
37+
const int* iter;
38+
39+
input_iter(const int* ii) : iter(ii) {}
40+
input_iter(const input_iter&amp;) = delete;
41+
input_iter(input_iter&amp;&amp;) = default;
42+
input_iter&amp; operator=(input_iter const&amp;) = delete;
43+
input_iter&amp; operator=(input_iter&amp;&amp;) = default;
44+
45+
using iterator_concept = std::input_iterator_tag;
46+
using difference_type = std::ptrdiff_t;
47+
using value_type = int;
48+
49+
const int&amp; operator*() const { return *iter; }
50+
input_iter&amp; operator++() { ++iter; return *this; }
51+
void operator++(int) { ++iter; }
52+
};
53+
54+
struct sent
55+
{
56+
const int* iter;
57+
58+
friend bool operator==(const input_iter&amp; i, const sent&amp; s) {
59+
return i.iter == s.iter;
60+
}
61+
62+
};
63+
64+
static_assert(std::input_iterator&lt;input_iter&gt;);
65+
static_assert(!std::forward_iterator&lt;input_iter&gt;);
66+
static_assert(std::sentinel_for&lt;sent, input_iter&gt;);
67+
68+
struct r : std::ranges::view_base
69+
{
70+
int* iter;
71+
size_t size;
72+
73+
template&lt;std::size_t N&gt;
74+
r(int (&amp;i)[N]) : iter(i), size(N){}
75+
76+
auto begin() { return iter; }
77+
auto end() { return iter + size; }
78+
79+
auto begin() const { return input_iter{iter}; }
80+
auto end() const { return sent{iter + size}; }
81+
82+
};
83+
84+
static_assert(std::ranges::range&lt;r&gt;);
85+
static_assert(std::ranges::range&lt;const r&gt;);
86+
87+
int main() {
88+
int input[] = { 1, 2, 3, 4, 5 };
89+
auto v = r{input} | std::views::adjacent&lt;2&gt;;
90+
for (auto&amp;&amp; t : v) {} // ok
91+
auto it = std::as_const(v).begin(); // <span style="color:#C80000;font-weight:bold">ill-formed</span>
92+
}
93+
</pre></blockquote>
94+
<p>
95+
<b>Daniel:</b>
96+
<p/>
97+
This issue has considerable wording overlap with LWG <iref ref="3731"/>.
98+
</p>
99+
</discussion>
100+
101+
<resolution>
102+
<p>
103+
This wording is relative to <paper num="N5014"/>.
104+
</p>
105+
106+
<ol>
107+
<li><p>Modify <sref ref="[range.adjacent.view]"/>, class template `adjacent_view` synopsis, as indicated:</p>
108+
109+
<blockquote>
110+
<pre>
111+
namespace std::ranges {
112+
template&lt;forward_range V, size_t N&gt;
113+
requires view&lt;V&gt; &amp;&amp; (N &gt; 0)
114+
class adjacent_view : public view_interface&lt;adjacent_view&lt;V, N&gt;&gt; {
115+
[&hellip;]
116+
public:
117+
[&hellip;]
118+
constexpr auto begin() requires (!<i>simple-view</i>&lt;V&gt;) {
119+
return <i>iterator</i>&lt;false&gt;(ranges::begin(<i>base_</i>), ranges::end(<i>base_</i>));
120+
}
121+
122+
constexpr auto begin() const requires <ins>forward_</ins>range&lt;const V&gt; {
123+
return <i>iterator</i>&lt;true&gt;(ranges::begin(<i>base_</i>), ranges::end(<i>base_</i>));
124+
}
125+
126+
constexpr auto end() requires (!<i>simple-view</i>&lt;V&gt;) {
127+
if constexpr (common_range&lt;V&gt;) {
128+
return <i>iterator</i>&lt;false&gt;(<i>as-sentinel</i>{}, ranges::begin(<i>base_</i>), ranges::end(<i>base_</i>));
129+
} else {
130+
return <i>sentinel</i>&lt;false&gt;(ranges::end(<i>base_</i>));
131+
}
132+
}
133+
134+
constexpr auto end() const requires <ins>forward_</ins>range&lt;const V&gt; {
135+
if constexpr (common_range&lt;const V&gt;) {
136+
return <i>iterator</i>&lt;true&gt;(<i>as-sentinel</i>{}, ranges::begin(<i>base_</i>), ranges::end(<i>base_</i>));
137+
} else {
138+
return <i>sentinel</i>&lt;true&gt;(ranges::end(<i>base_</i>));
139+
}
140+
}
141+
[&hellip;]
142+
};
143+
}
144+
</pre>
145+
</blockquote>
146+
</li>
147+
</ol>
148+
149+
</resolution>
150+
151+
</issue>

0 commit comments

Comments
 (0)