Skip to content

Commit f1f8275

Browse files
committed
New issue from Hewill Kang: "possibly-const-range should prefer returning const R&"
1 parent 52903cf commit f1f8275

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed

xml/issue4027.xml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4027" status="New">
5+
<title><tt><i>possibly-const-range</i></tt> should prefer returning <tt>const R&amp;</tt></title>
6+
<section><sref ref="[ranges.syn]"/></section>
7+
<submitter>Hewill Kang</submitter>
8+
<date>17 Dec 2023</date>
9+
<priority>99</priority>
10+
11+
<discussion>
12+
<p>
13+
<tt><i>possibly-const-range</i></tt> currently only returns <tt>const R&amp;</tt> when <tt>R</tt> does not
14+
satisfy <tt>constant_range</tt> and <tt>const R</tt> satisfies <tt>constant_range</tt>.
15+
<p/>
16+
Although it's not clear why we need the former condition, this does diverge from the legacy <tt>std::cbegin</tt>
17+
(<a href="https://godbolt.org/z/636osY7os">demo</a>):
18+
</p>
19+
<blockquote><pre>
20+
#include &lt;ranges&gt;
21+
22+
int main() {
23+
auto r = std::views::single(0)
24+
| std::views::transform([](int) { return 0; });
25+
using C1 = decltype(std::ranges::cbegin(r));
26+
using C2 = decltype(std::cbegin(r));
27+
static_assert(std::same_as&lt;C1, C2&gt;); // <span style="color:red;font-weight:bolder">failed</span>
28+
}
29+
</pre></blockquote>
30+
<p>
31+
Since <tt>R</tt> itself is <tt>constant_range</tt>, so <tt><i>possibly-const-range</i></tt>, above just returns
32+
<tt>R&amp;</tt> and <tt>C1</tt> is <tt>transform_view::<i>iterator</i>&lt;false&gt;</tt>; <tt>std::cbegin</tt>
33+
specifies to return <tt>as_const(r).begin()</tt>, which makes that <tt>C2</tt> is
34+
<tt>transform_view::<i>iterator</i>&lt;true&gt;</tt> which is different from <tt>C1</tt>.
35+
<p/>
36+
I believe <tt>const R&amp;</tt> should always be returned if it's a range, regardless of whether <tt>const R</tt>
37+
or <tt>R</tt> is a <tt>constant_range</tt>, just as <tt><i>fmt-maybe-const</i></tt> in format ranges always prefers
38+
<tt>const R</tt> over <tt>R</tt>.
39+
<p/>
40+
Although it is theoretically possible for <tt>R</tt> to satisfy <tt>constant_range</tt> and that <tt>const R</tt>
41+
is a mutable range, such nonsense range type should not be of interest.
42+
<p/>
43+
This relaxation of constraints allows for maximum consistency with <tt>std::cbegin</tt>, and in some cases can
44+
preserve constness to the greatest extent (<a href="https://godbolt.org/z/3hYToMq35">demo</a>):
45+
</p>
46+
<blockquote><pre>
47+
#include &lt;ranges&gt;
48+
49+
int main() {
50+
auto r = std::views::single(0) | std::views::lazy_split(0);
51+
(*std::ranges::cbegin(r)).front() = 42; // ok
52+
(*std::cbegin(r)).front() = 42; // <span style="color:red;font-weight:bolder">not ok</span>
53+
}
54+
</pre></blockquote>
55+
<p>
56+
Above, <tt>*std::ranges::cbegin</tt> returns a range of type <tt>const lazy_split_view::<i>outer-iterator</i>&lt;false&gt;::value_type</tt>,
57+
which does not satisfy <tt>constant_range</tt> because its reference type is <tt>int&amp;</tt>.
58+
<p/>
59+
However, <tt>*std::cbegin(r)</tt> returns <tt>lazy_split_view::<i>outer-iterator</i>&lt;true&gt;::value_type</tt>
60+
whose reference type is <tt>const int&amp;</tt> and satisfies <tt>constant_range</tt>.
61+
</p>
62+
</discussion>
63+
64+
<resolution>
65+
<p>
66+
This wording is relative to <paper num="N4971"/>.
67+
</p>
68+
69+
<ol>
70+
71+
<li><p>Modify <sref ref="[ranges.syn]"/>, header <tt>&lt;ranges&gt;</tt> synopsis, as indicated:</p>
72+
73+
<blockquote>
74+
<pre>
75+
#include &lt;compare&gt; // <i>see <sref ref="[compare.syn]"/></i>
76+
#include &lt;initializer_list&gt; // <i>see <sref ref="[initializer.list.syn]"/></i>
77+
#include &lt;iterator&gt; // <i>see <sref ref="[iterator.synopsis]"/></i>
78+
79+
namespace std::ranges {
80+
[&hellip;]
81+
82+
// <i><sref ref="[range.as.const]"/>, as const view</i>
83+
template&lt;input_range R&gt;
84+
constexpr auto&amp; <i>possibly-const-range</i>(R&amp; r) noexcept { // <i>exposition only</i>
85+
if constexpr (<ins>input</ins><del>constant</del>_range&lt;const R&gt;<del> &amp;&amp; !constant_range&lt;R&gt;</del>) {
86+
return const_cast&lt;const R&amp;&gt;(r);
87+
} else {
88+
return r;
89+
}
90+
}
91+
92+
[&hellip;]
93+
}
94+
</pre>
95+
</blockquote>
96+
</li>
97+
98+
</ol>
99+
100+
101+
</resolution>
102+
103+
</issue>

0 commit comments

Comments
 (0)