Skip to content

Commit 346e759

Browse files
committed
New issue from Patrick Palka: "Constraint recursion in basic_const_iterator's relational operators due to ADL + CWG 2369"
1 parent 5e86be5 commit 346e759

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed

xml/issue4218.xml

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4218" status="New">
5+
<title>Constraint recursion in `basic_const_iterator`'s relational operators due to ADL + CWG 2369</title>
6+
<section>
7+
<sref ref="[const.iterators.ops]"/>
8+
</section>
9+
<submitter>Patrick Palka</submitter>
10+
<date>03 Mar 2025</date>
11+
<priority>99</priority>
12+
13+
<discussion>
14+
<p>
15+
Consider the example (devised by Hewill Kang)
16+
</p>
17+
<blockquote><pre>
18+
using RCI = reverse_iterator&lt;basic_const_iterator&lt;vector&lt;int&gt;::iterator&gt;&gt;;
19+
static_assert(std::totally_ordered&lt;RCI&gt;);
20+
</pre></blockquote>
21+
<p>
22+
Checking `RCI` is `totally_ordered` entails checking
23+
</p>
24+
<blockquote><pre>
25+
requires (RCI x) { x <i>RELOP</i> x; } for each <i>RELOP</i> in {&lt;, &gt;, &lt;=, &gt;=}
26+
</pre></blockquote>
27+
<p>
28+
which we expect to be straightforwardly satisfied by `reverse_iterator`'s
29+
namespace-scope operators (<sref ref="[reverse.iter.cmp]"/>):
30+
</p>
31+
<blockquote><pre>
32+
template&lt;class Iterator1, class Iterator2&gt;
33+
constexpr bool operator&lt;(
34+
const reverse_iterator&lt;Iterator1&gt;&amp; x,
35+
const reverse_iterator&lt;Iterator2&gt;&amp; y);
36+
// etc
37+
</pre></blockquote>
38+
<p>
39+
But due to ADL we find ourselves also considering the `basic_const_iterator`
40+
relop friends (<sref ref="[const.iterators.ops]"/>/24).
41+
</p>
42+
<blockquote><pre>
43+
template&lt;input_iterator Iterator&gt;
44+
class basic_const_iterator {
45+
template&lt;<i>not-a-const-iterator</i> I&gt;
46+
friend constexpr bool operator&lt;(const I&amp; x, const basic_const_iterator&amp; y)
47+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;
48+
// etc
49+
};
50+
</pre></blockquote>
51+
<p>
52+
Before <a href="https://wg21.link/cwg2369">CWG 2369</a> these candidates would quickly get
53+
discarded since for the second operand RCI clearly isn't convertible to `basic_const_iterator`.
54+
But after CWG 2369 implementations must first check these operators' constraints
55+
(with <tt>Iterator = vector&lt;int&gt;::iterator</tt> and <tt>I = RCI</tt>), which entails
56+
checking <tt>totally_ordered&lt;RCI&gt;</tt> recursively, causing the example to be ill-formed.
57+
<p/>
58+
The constraint recursion is diagnosed by GCC (<a href="https://godbolt.org/z/dr1dK1dnj">See godbolt demo</a>).
59+
Other compilers accept the example because they don't implement CWG 2369, as
60+
far as I know.
61+
<p/>
62+
GCC trunk works around this issue by giving these friend relational operators
63+
a dependent second operand of the form <tt>basic_const_iterator&lt;J&gt;</tt> where `J` is
64+
constrained to match `Iterator`:
65+
</p>
66+
<blockquote><pre>
67+
template&lt;<i>not-a-const-iterator</i> I, same_as&lt;Iterator&gt; J>
68+
friend constexpr bool operator&lt;(const I&amp; x, const basic_const_iterator&lt;J&gt;&amp; y)
69+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;
70+
// etc
71+
</pre></blockquote>
72+
<p>
73+
So that deduction fails earlier, before constraints get checked, for a second
74+
operand that isn't a specialization of `basic_const_iterator` (or derived from one).
75+
<p/>
76+
LWG <iref ref="3769"/> is an earlier issue about constraint recursion in `basic_const_iterator`'s
77+
operators, but there the recursion was independent of CWG 2369.
78+
</p>
79+
</discussion>
80+
81+
<resolution>
82+
<p>
83+
This wording is relative to <paper num="N5001"/>.
84+
</p>
85+
86+
<ol>
87+
<li><p>Modify <sref ref="[const.iterators.iterator]"/>, class template `basic_const_iterator` synopsis, as indicated:</p>
88+
89+
<blockquote>
90+
<pre>
91+
namespace std {
92+
[&hellip;]
93+
template&lt;input_iterator Iterator&gt;
94+
class basic_const_iterator {
95+
[&hellip;]
96+
template&lt;<i>not-a-const-iterator</i> I<ins>, same_as&lt;Iterator&gt; J</ins>&gt;
97+
friend constexpr bool operator&lt;(const I&amp; x, const basic_const_iterator<ins>&lt;J&gt;</ins>&amp; y)
98+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;;
99+
template&lt;<i>not-a-const-iterator</i> I<ins>, same_as&lt;Iterator&gt; J</ins>&gt;
100+
friend constexpr bool operator&gt;(const I&amp; x, const basic_const_iterator<ins>&lt;J&gt;</ins>&amp; y)
101+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;;
102+
template&lt;<i>not-a-const-iterator</i> I<ins>, same_as&lt;Iterator&gt; J</ins>&gt;
103+
friend constexpr bool operator&lt;=(const I&amp; x, const basic_const_iterator<ins>&lt;J&gt;</ins>&amp; y)
104+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;;
105+
template&lt;<i>not-a-const-iterator</i> I<ins>, same_as&lt;Iterator&gt; J</ins>&gt;
106+
friend constexpr bool operator&gt;=(const I&amp; x, const basic_const_iterator<ins>&lt;J&gt;</ins>&amp; y)
107+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;;
108+
[&hellip;]
109+
};
110+
}
111+
</pre>
112+
</blockquote>
113+
</li>
114+
115+
<li><p>Modify <sref ref="[const.iterators.ops]"/> as indicated:</p>
116+
117+
<blockquote>
118+
<pre>
119+
template&lt;<i>not-a-const-iterator</i> I<ins>, same_as&lt;Iterator&gt; J</ins>&gt;
120+
friend constexpr bool operator&lt;(const I&amp; x, const basic_const_iterator<ins>&lt;J&gt;</ins>&amp; y)
121+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;;
122+
template&lt;<i>not-a-const-iterator</i> I<ins>, same_as&lt;Iterator&gt; J</ins>&gt;
123+
friend constexpr bool operator&gt;(const I&amp; x, const basic_const_iterator<ins>&lt;J&gt;</ins>&amp; y)
124+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;;
125+
template&lt;<i>not-a-const-iterator</i> I<ins>, same_as&lt;Iterator&gt; J</ins>&gt;
126+
friend constexpr bool operator&lt;=(const I&amp; x, const basic_const_iterator<ins>&lt;J&gt;</ins>&amp; y)
127+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;;
128+
template&lt;<i>not-a-const-iterator</i> I<ins>, same_as&lt;Iterator&gt; J</ins>&gt;
129+
friend constexpr bool operator&gt;=(const I&amp; x, const basic_const_iterator<ins>&lt;J&gt;</ins>&amp; y)
130+
requires random_access_iterator&lt;Iterator&gt; &amp;&amp; totally_ordered_with&lt;Iterator, I&gt;;
131+
</pre>
132+
<blockquote>
133+
<p>
134+
-23- Let <tt><i>op</i></tt> be the operator.
135+
<p/>
136+
-24- <i>Effects</i>: Equivalent to: <tt>return x <i>op</i> y.<i>current_</i>;</tt>
137+
</p>
138+
</blockquote>
139+
</blockquote>
140+
</li>
141+
</ol>
142+
143+
</resolution>
144+
145+
</issue>

0 commit comments

Comments
 (0)