forked from lwg/issues
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New issue from S.B. Tam: "§[range.utility.conv.to] ranges::to may cau…
…se infinite recursion if range_value_t<C> is a non-move-constructible range"
- Loading branch information
Showing
1 changed file
with
147 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
<?xml version='1.0' encoding='utf-8' standalone='no'?> | ||
<!DOCTYPE issue SYSTEM "lwg-issue.dtd"> | ||
|
||
<issue num="4008" status="New"> | ||
<title>§[range.utility.conv.to] <tt>ranges::to</tt> may cause infinite recursion if <tt>range_value_t<C></tt> | ||
is a non-move-constructible range</title> | ||
<section><sref ref="[range.utility.conv.to]"/></section> | ||
<submitter>S. B. Tam</submitter> | ||
<date>08 Nov 2023</date> | ||
<priority>99</priority> | ||
|
||
<discussion> | ||
<p> | ||
<sref ref="[range.utility.conv.to]"/>/2 says: | ||
</p> | ||
<blockquote> | ||
<ol style="list-style-type: none"> | ||
<li><p>(2.1) — If <tt>C</tt> does not satisfy <tt>input_range</tt> or | ||
<tt>convertible_to<range_reference_t<R>, range_value_t<C>></tt> is <tt>true</tt>:</p> | ||
<ol style="list-style-type: none"> | ||
<li><p>[…]</p></li> | ||
</ol></li> | ||
<li><p>(2.2) — Otherwise, if <tt>input_range<range_reference_t<R>></tt> is <tt>true</tt>: | ||
</p> | ||
<blockquote> | ||
<pre> | ||
to<C>(r | views::transform([](auto&& elem) { | ||
return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); | ||
}), std::forward<Args>(args)...); | ||
</pre> | ||
</blockquote> | ||
</li> | ||
<li><p>[…]</p></li> | ||
</ol> | ||
</blockquote> | ||
<p> | ||
That is, if <tt>range_reference_t<R></tt> is not convertible to <tt>range_value_t<C></tt>, and | ||
<tt>range_reference_t<R></tt> is an input range, <tt>views::transform</tt> is applied to convert the | ||
inner range to <tt>range_value_t<C></tt> (through <tt>to<range_value_t<C>></tt>), and then | ||
the transformed range is converted to <tt>C</tt> (through <tt>to<C></tt>). | ||
<p/> | ||
Consider: | ||
</p> | ||
<blockquote><pre> | ||
#include <ranges> | ||
|
||
struct ImmovableRange { | ||
ImmovableRange(int*, int*); | ||
ImmovableRange(ImmovableRange&&) = delete; | ||
|
||
int* begin(); | ||
int* end(); | ||
}; | ||
|
||
struct C { | ||
ImmovableRange* begin(); | ||
ImmovableRange* end(); | ||
}; | ||
|
||
using R = int[1][2]; | ||
|
||
void test() { | ||
(void)std::ranges::to<C>(R{}); | ||
} | ||
</pre></blockquote> | ||
<p> | ||
Here: | ||
</p> | ||
<ol> | ||
<li><p><tt>convertible_to<range_reference_t<R>, range_value_t<C>></tt> is <tt>false</tt>.</p></li> | ||
<li><p><tt>range_reference_t<R></tt> satisfies <tt>input_range</tt>.</p></li> | ||
<li><p><tt>range_reference_t<R></tt> can be converted to <tt>range_value_t<C></tt> through | ||
<tt>to<range_value_t<C>></tt>. (If it couldn't, an error would be produced immediately.)</p></li> | ||
</ol> | ||
<p> | ||
So <tt>to<C></tt> is called recursively, constructing <tt>C</tt> with the transformed range (whose | ||
<tt>range_reference_t<R></tt> is the same as <tt>range_value_t<C></tt>). For the construction | ||
from the transformed range: | ||
</p> | ||
<ol> | ||
<li><p><tt>range_reference_t<R></tt> and <tt>range_value_t<C></tt> are both <tt>ImmovableRange</tt>.</p></li> | ||
<li><p><tt>convertible_to<range_reference_t<R>, range_value_t<C>></tt> (i.e. | ||
<tt>convertible_to<ImmovableRange, ImmovableRange></tt>) is <tt>false</tt>.</p></li> | ||
<li><p><tt>range_reference_t<R></tt> (i.e. <tt>ImmovableRange</tt>) satisfies <tt>input_range</tt>.</p></li> | ||
<li><p><tt>range_reference_t<R></tt> can be converted to <tt>range_value_t<C></tt> through | ||
<tt>to<range_value_t<C>></tt>.</p></li> | ||
</ol> | ||
<p> | ||
So <tt>to<C></tt> is called recursively again, transforming the range for the second time. This time, | ||
the transformation does not change any of the above four facts. As a result, <tt>to<C></tt> is called | ||
yet again, leading to an infinite recursion. | ||
<p/> | ||
I believe this can be fixed by stop calling <tt>to<C></tt> recursively when <tt>range_reference_t<R></tt> | ||
is the same as <tt>range_value_t<C></tt>. | ||
</p> | ||
</discussion> | ||
|
||
<resolution> | ||
<p> | ||
This wording is relative to <paper num="N4964"/>. | ||
</p> | ||
|
||
<ol> | ||
|
||
<li><p>Modify <sref ref="[range.utility.conv.to]"/> as indicated:</p> | ||
|
||
<blockquote> | ||
<pre> | ||
template<class C, input_range R, class... Args> requires (!view<C>) | ||
constexpr C to(R&& r, Args&&... args); | ||
</pre> | ||
<blockquote> | ||
<p> | ||
-1- <i>Mandates</i>: <tt>C</tt> is a cv-unqualified class type. | ||
</p> | ||
<p> | ||
-2- <i>Returns</i>: An object of type <tt>C</tt> constructed from the elements of <tt>r</tt> in the following manner: | ||
</p> | ||
<ol style="list-style-type: none"> | ||
<li><p>(2.1) — If <tt>C</tt> does not satisfy <tt>input_range</tt> or | ||
<tt>convertible_to<range_reference_t<R>, range_value_t<C>></tt> is <tt>true</tt>:</p> | ||
<ol style="list-style-type: none"> | ||
<li><p>[…]</p></li> | ||
</ol></li> | ||
<li><p>(2.2) — Otherwise, if <ins><tt>same_as<range_reference_t<R>, range_value_t<C>></tt> | ||
is <tt>false</tt> and</ins> <tt>input_range<range_reference_t<R>></tt> is <tt>true</tt>: | ||
</p> | ||
<blockquote> | ||
<pre> | ||
to<C>(r | views::transform([](auto&& elem) { | ||
return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); | ||
}), std::forward<Args>(args)...); | ||
</pre> | ||
</blockquote> | ||
</li> | ||
<li><p>(2.3) — Otherwise, the program is ill-formed.</p></li> | ||
</ol> | ||
</blockquote> | ||
</blockquote> | ||
|
||
|
||
</li> | ||
|
||
</ol> | ||
</resolution> | ||
|
||
</issue> |