<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 3743: ranges::to's reserve may be ill-formed</title>
<meta property="og:title" content="Issue 3743: ranges::to's reserve may be ill-formed">
<meta property="og:description" content="C++ library issue. Status: C++23">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue3743.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#C++23">C++23</a> status.</em></p>
<h3 id="3743"><a href="lwg-defects.html#3743">3743</a>. <code>ranges::to</code>'s <code>reserve</code> may be ill-formed</h3>
<p><b>Section:</b> 25.5.7.2 <a href="https://wg21.link/range.utility.conv.to">[range.utility.conv.to]</a> <b>Status:</b> <a href="lwg-active.html#C++23">C++23</a>
 <b>Submitter:</b> Hewill Kang <b>Opened:</b> 2022-07-21 <b>Last modified:</b> 2024-01-29</p>
<p><b>Priority: </b>Not Prioritized
</p>
<p><b>View other</b> <a href="lwg-index-open.html#range.utility.conv.to">active issues</a> in [range.utility.conv.to].</p>
<p><b>View all other</b> <a href="lwg-index.html#range.utility.conv.to">issues</a> in [range.utility.conv.to].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#C++23">C++23</a> status.</p>
<p><b>Discussion:</b></p>
<p>
When the "reserve" branch is satisfied, <code>ranges::to</code> directly passes the 
result of <code>ranges::size(r)</code> into the <code>reserve</code> call. However, given 
that the standard only guarantees that integer-class type can be explicitly converted 
to any integer-like type (24.3.4.4 <a href="https://wg21.link/iterator.concept.winc">[iterator.concept.winc]</a> p6), this makes 
the call potentially ill-formed, since <code>ranges::size(r)</code> may return an 
integer-class type:
</p>
<blockquote><pre>
#include &lt;ranges&gt;
#include &lt;vector&gt;

int main() {
  auto r = std::ranges::subrange(std::views::iota(0ULL) | std::views::take(5), 5);
  auto v = r | std::ranges::to&lt;std::vector&lt;std::size_t&gt;&gt;(0); // <span style="color:red;font-weight:bolder">cannot implicitly convert _Unsigned128 to size_t in MSVC-STL</span>
}
</pre></blockquote>
<p>
We should do an explicit cast before calling <code>reserve</code>.
</p>

<p><i>[2022-08-23; Reflector poll]</i></p>

<p>
Set status to Tentatively Ready after six votes in favour during reflector poll.
</p>
<p>
Are we all happy that the result of conversion to the container's size type
may be less than the length of the source range, so the reservation is too small
but we don't realize until pushing the max_size() + 1st element fails?
I think it's acceptable that converting pathologically large ranges to
containers fails kind of messily, but I could imagine throwing
if the range length is greater than container's max_size().
</p>

<p><i>[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting &rarr; WP.]</i></p>



<p id="res-3743"><b>Proposed resolution:</b></p>
<p>
This wording is relative to <a href="https://wg21.link/N4910" title=" Working Draft, Standard for Programming Language C++">N4910</a>.
</p>

<ol>
<li><p>Modify 25.5.7.2 <a href="https://wg21.link/range.utility.conv.to">[range.utility.conv.to]</a> as indicated:</p>

<blockquote>
<pre>
template&lt;class C, input_range R, class... Args&gt; requires (!view&lt;C&gt;)
  constexpr C to(R&amp;&amp; r, Args&amp;&amp;... args);
</pre>
<blockquote>
<p>
-1- <i>Returns</i>: An object of type <code>C</code> constructed from the elements of <code>r</code> in the following manner:
</p>
<ol style="list-style-type: none">
<li><p>(1.1) &mdash; If <code>convertible_to&lt;range_reference_t&lt;R&gt;, range_value_t&lt;C&gt;&gt;</code> is <code>true</code>:</p>
<ol style="list-style-type: none">
<li><p>(1.1.1) &mdash; If <code>constructible_from&lt;C, R, Args...&gt;</code> is <code>true</code>:</p>
<blockquote><code>C(std::forward&lt;R&gt;(r), std::forward&lt;Args&gt;(args)...)</code></blockquote></li>
<li><p>(1.1.2) &mdash; Otherwise, if <code>constructible_from&lt;C, from_range_t, R, Args...&gt;</code> is <code>true</code>:</p>
<blockquote><code>C(from_range, std::forward&lt;R&gt;(r), std::forward&lt;Args&gt;(args)...)</code></blockquote></li>
<li><p>(1.1.3) &mdash; Otherwise, if</p>
<ol style="list-style-type: none">
<li><p>(1.1.3.1) &mdash; <code>common_range&lt;R&gt;</code> is <code>true</code>,</p></li>
<li><p>(1.1.3.2) &mdash; <code><i>cpp17-input-iterator</i>&lt;iterator_t&lt;R&gt;&gt;</code> is <code>true</code>, and</p></li>
<li><p>(1.1.3.3) &mdash; <code>constructible_from&lt;C, iterator_t&lt;R&gt;, sentinel_t&lt;R&gt;, Args...&gt;</code> is <code>true</code>:</p>
<blockquote><code>C(ranges::begin(r), ranges::end(r), std::forward&lt;Args&gt;(args)...)</code></blockquote></li>
</ol>
</li>
<li><p>(1.1.4) &mdash; Otherwise, if</p>
<ol style="list-style-type: none">
<li><p>(1.1.4.1) &mdash; <code>constructible_from&lt;C, Args...&gt;</code> is <code>true</code>, and</p></li>
<li><p>(1.1.4.2) &mdash; <code><i>container-insertable</i>&lt;C, range_reference_t&lt;R&gt;&gt;</code> is <code>true</code>:</p>
<blockquote><pre>
C c(std::forward&lt;Args&gt;(args)...);
if constexpr (sized_range&lt;R&gt; &amp;&amp; <i>reservable-container</i>&lt;C&gt;)
  c.reserve(<ins>static_cast&lt;range_size_t&lt;C&gt;&gt;(</ins>ranges::size(r)<ins>)</ins>);
ranges::copy(r, <i>container-inserter</i>&lt;range_reference_t&lt;R&gt;&gt;(c));
</pre></blockquote></li>
</ol>
</li>
</ol>
</li>
<li><p>(1.2) &mdash; Otherwise, if <code>input_range&lt;range_reference_t&lt;R&gt;&gt;</code> is <code>true</code>:</p>
<blockquote><pre>
to&lt;C&gt;(r | views::transform([](auto&amp;&amp; elem) {
  return to&lt;range_value_t&lt;C&gt;&gt;(std::forward&lt;decltype(elem)&gt;(elem));
}), std::forward&lt;Args&gt;(args)...);
</pre></blockquote></li>
<li><p>(1.3) &mdash; Otherwise, the program is ill-formed.</p></li>
</ol>
</blockquote>
</blockquote>
</li>
</ol>





</body>
</html>
