Skip to content

Commit

Permalink
Adding this other note
Browse files Browse the repository at this point in the history
  • Loading branch information
brevzin committed Mar 8, 2024
1 parent cb65944 commit f428e14
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 15 deletions.
15 changes: 8 additions & 7 deletions 3177_const_prvalue/const-prvalue.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ The issue at hand is about this:
template <class T>
auto make() -> T;

template <class T, class U>
using cond = decltype(true ? make<T>() : make<U>());

template <class T>
using cref = decltype(true ? make<T>() : make<T const&>());
using cref = cond<T, T consT&>;
```
:::
Expand Down Expand Up @@ -109,13 +112,11 @@ template <class T>
auto make() -> T;
template <class I>
using const_reference_t = decltype(true
? make<iter_value_t<I> const&&>()
: make<iter_reference_t<I>>());
using const_reference_t = cond<iter_value_t<I> const&&, iter_reference_t<I>>;
```
:::

Which, for `Priterator<T>`, is `decltype(true ? make<T const&&>() : make<T>())`.
Which, for `Priterator<T>`, is `cond<T const&&, T>`.

This is the same construct we saw at the beginning of the paper, just that it's a `T const&&` instead of a `T const&`. But the rules end up being the same: if `T` is a scalar type, `const_reference_t<T>` is `T`. If `T` is a class type, then `const_reference_t<T>` is `T const`.

Expand Down Expand Up @@ -160,6 +161,6 @@ The *weak* proposal: if both operands have the same underlying type (excluding v

The *strong* proposal: if both operands have the same underlying type (excluding value category and const), then the result of the conditional operator should never be a const prvalue (i.e. do as the `int`s do).

Put differently, the weak proposal only addresses the [orange]{.orange} entries. The strong proposal addresses both the [orange]{.orange} and [yellow]{.yellow} ones.
Put differently, the weak proposal only addresses the [orange]{.orange} entries. The strong proposal addresses both the [orange]{.orange} and [yellow]{.yellow} ones. The weak proposal is sufficient to address the [pessimizing assignment issue](#pessimizing-assignment) and the [const wrapping issue](#extra-wrapping).

The weak proposal is sufficient to address the [pessimizing assignment issue](#pessimizing-assignment) and the [const wrapping issue](#extra-wrapping).
Notably, one odd quirk of the strong proposal is that the type of `cond<T, T>` is `T` for all types, value categories, and const. Except one: `T const`. In the strong proposal, `cond<T const, T const>` becomes `T`. Now, it technically is already just `T` for scalar types - but it's not possible to even have a const prvalue of scalar type, so this doesn't really matter. So we could alter the strong proposal to say that `cond<T, U>` is only ever a const prvalue in the specific case of `cond<T const, T const>` - otherwise all prvalues are non-const. This would still technically give different answers between scalar and class types, but not in a meaningfully observed way.
17 changes: 9 additions & 8 deletions 3177_const_prvalue/p3177r0.html
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,11 @@ <h1 data-number="1" style="border-bottom:1px solid #cccccc" id="introduction"><s
<span id="cb1-2"><a href="#cb1-2"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="kw">auto</span> make<span class="op">()</span> <span class="op">-&gt;</span> T;</span>
<span id="cb1-4"><a href="#cb1-4"></a></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="kw">using</span> cref <span class="op">=</span> <span class="kw">decltype</span><span class="op">(</span><span class="kw">true</span> <span class="op">?</span> make<span class="op">&lt;</span>T<span class="op">&gt;()</span> <span class="op">:</span> make<span class="op">&lt;</span>T <span class="kw">const</span><span class="op">&amp;&gt;())</span>;</span></code></pre></div>
<span id="cb1-5"><a href="#cb1-5"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> T, <span class="kw">class</span> U<span class="op">&gt;</span></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="kw">using</span> cond <span class="op">=</span> <span class="kw">decltype</span><span class="op">(</span><span class="kw">true</span> <span class="op">?</span> make<span class="op">&lt;</span>T<span class="op">&gt;()</span> <span class="op">:</span> make<span class="op">&lt;</span>U<span class="op">&gt;())</span>;</span>
<span id="cb1-7"><a href="#cb1-7"></a></span>
<span id="cb1-8"><a href="#cb1-8"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb1-9"><a href="#cb1-9"></a><span class="kw">using</span> cref <span class="op">=</span> cond<span class="op">&lt;</span>T, T consT<span class="op">&amp;&gt;</span>;</span></code></pre></div>
</blockquote>
<p>What do you expect the type of <code class="sourceCode cpp">cref<span class="op">&lt;</span>T<span class="op">&gt;</span></code> to be? Well, it’s obviously some kind of a <code class="sourceCode cpp">T</code> (since both sides are <code class="sourceCode cpp">T</code>, so nothing to convert). It cannot be <code class="sourceCode cpp">T<span class="op">&amp;</span></code> (which cannot bind to either operand) or <code class="sourceCode cpp">T<span class="op">&amp;&amp;</span></code> (which cannot bind to <code class="sourceCode cpp">T <span class="kw">const</span><span class="op">&amp;</span></code>). It could potentially have been <code class="sourceCode cpp">T <span class="kw">const</span><span class="op">&amp;</span></code> (to do conditional lifetime extension) but it would be odd to elevate the prvalue to lvalue in this context. <code class="sourceCode cpp">T <span class="kw">const</span><span class="op">&amp;&amp;</span></code> is a type that most people never think of, and I think in this context we should also not think about it.</p>
<p>I think at this point many people would expect that this leaves <code class="sourceCode cpp">T</code> amongst the options and conclude that <code class="sourceCode cpp">cref<span class="op">&lt;</span>T<span class="op">&gt;</span></code> is simply <code class="sourceCode cpp">T</code>. But it turns out that’s not quite the case. For <em>scalar types</em>, <code class="sourceCode cpp">cref<span class="op">&lt;</span>T<span class="op">&gt;</span></code> is <code class="sourceCode cpp">T</code>. But for <em>class types</em>, <code class="sourceCode cpp">cref<span class="op">&lt;</span>T<span class="op">&gt;</span></code> is actually <code class="sourceCode cpp">T <span class="kw">const</span></code>.</p>
Expand Down Expand Up @@ -672,11 +675,9 @@ <h2 data-number="1.2" id="extra-wrapping"><span class="header-section-number">1.
<span id="cb5-2"><a href="#cb5-2"></a><span class="kw">auto</span> make<span class="op">()</span> <span class="op">-&gt;</span> T;</span>
<span id="cb5-3"><a href="#cb5-3"></a></span>
<span id="cb5-4"><a href="#cb5-4"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> I<span class="op">&gt;</span></span>
<span id="cb5-5"><a href="#cb5-5"></a><span class="kw">using</span> const_reference_t <span class="op">=</span> <span class="kw">decltype</span><span class="op">(</span><span class="kw">true</span></span>
<span id="cb5-6"><a href="#cb5-6"></a> <span class="op">?</span> make<span class="op">&lt;</span>iter_value_t<span class="op">&lt;</span>I<span class="op">&gt;</span> <span class="kw">const</span><span class="op">&amp;&amp;&gt;()</span></span>
<span id="cb5-7"><a href="#cb5-7"></a> <span class="op">:</span> make<span class="op">&lt;</span>iter_reference_t<span class="op">&lt;</span>I<span class="op">&gt;&gt;())</span>;</span></code></pre></div>
<span id="cb5-5"><a href="#cb5-5"></a><span class="kw">using</span> const_reference_t <span class="op">=</span> cond<span class="op">&lt;</span>iter_value_t<span class="op">&lt;</span>I<span class="op">&gt;</span> <span class="kw">const</span><span class="op">&amp;&amp;</span>, iter_reference_t<span class="op">&lt;</span>I<span class="op">&gt;&gt;</span>;</span></code></pre></div>
</blockquote>
<p>Which, for <code class="sourceCode cpp">Priterator<span class="op">&lt;</span>T<span class="op">&gt;</span></code>, is <code class="sourceCode cpp"><span class="kw">decltype</span><span class="op">(</span><span class="kw">true</span> <span class="op">?</span> make<span class="op">&lt;</span>T <span class="kw">const</span><span class="op">&amp;&amp;&gt;()</span> <span class="op">:</span> make<span class="op">&lt;</span>T<span class="op">&gt;())</span></code>.</p>
<p>Which, for <code class="sourceCode cpp">Priterator<span class="op">&lt;</span>T<span class="op">&gt;</span></code>, is <code class="sourceCode cpp">cond<span class="op">&lt;</span>T <span class="kw">const</span><span class="op">&amp;&amp;</span>, T<span class="op">&gt;</span></code>.</p>
<p>This is the same construct we saw at the beginning of the paper, just that it’s a <code class="sourceCode cpp">T <span class="kw">const</span><span class="op">&amp;&amp;</span></code> instead of a <code class="sourceCode cpp">T <span class="kw">const</span><span class="op">&amp;</span></code>. But the rules end up being the same: if <code class="sourceCode cpp">T</code> is a scalar type, <code class="sourceCode cpp">const_reference_t<span class="op">&lt;</span>T<span class="op">&gt;</span></code> is <code class="sourceCode cpp">T</code>. If <code class="sourceCode cpp">T</code> is a class type, then <code class="sourceCode cpp">const_reference_t<span class="op">&lt;</span>T<span class="op">&gt;</span></code> is <code class="sourceCode cpp">T <span class="kw">const</span></code>.</p>
<p>The result of this is that <code class="sourceCode cpp">Priterator<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span></code> <em>is</em> considered a constant iterator (because its <code class="sourceCode cpp">iter_const_reference_t</code> is <code class="sourceCode cpp"><span class="dt">int</span></code>, which is the same as its reference type) while <code class="sourceCode cpp">Priterator<span class="op">&lt;</span>SomeClass<span class="op">&gt;</span></code> <em>is not</em> considered a constant iterator (because its <code class="sourceCode cpp">iter_const_reference_t</code> becomes <code class="sourceCode cpp">SomeClass <span class="kw">const</span></code>, which is now a different type). Which means that a range of prvalue class type isn’t considered a constant range, and <code class="sourceCode cpp">views<span class="op">::</span>as_const</code> would pointlessly wrap it.</p>
<p>This isn’t just unnecessary wrapping and template instantiation - this now means instead of a range of prvalues, we end up with a range of <em>const</em> prvalues - which means copying when we could have potentially been moving.</p>
Expand Down Expand Up @@ -846,8 +847,8 @@ <h1 data-number="3" style="border-bottom:1px solid #cccccc" id="proposal"><span
<p>There are two potential proposals here: the <em>weak</em> proposal and the <em>strong</em> proposal.</p>
<p>The <em>weak</em> proposal: if both operands have the same underlying type (excluding value category and const), then the result of the conditional operator should only be a const prvalue if at least one of the operands is a const prvalue. Otherwise, it should be a non-const prvalue.</p>
<p>The <em>strong</em> proposal: if both operands have the same underlying type (excluding value category and const), then the result of the conditional operator should never be a const prvalue (i.e. do as the <code class="sourceCode cpp"><span class="dt">int</span></code>s do).</p>
<p>Put differently, the weak proposal only addresses the <span class="orange">orange</span> entries. The strong proposal addresses both the <span class="orange">orange</span> and <span class="yellow">yellow</span> ones.</p>
<p>The weak proposal is sufficient to address the <a href="#pessimizing-assignment">pessimizing assignment issue</a> and the <a href="#extra-wrapping">const wrapping issue</a>.</p>
<p>Put differently, the weak proposal only addresses the <span class="orange">orange</span> entries. The strong proposal addresses both the <span class="orange">orange</span> and <span class="yellow">yellow</span> ones. The weak proposal is sufficient to address the <a href="#pessimizing-assignment">pessimizing assignment issue</a> and the <a href="#extra-wrapping">const wrapping issue</a>.</p>
<p>Notably, one odd quirk of the strong proposal is that the type of <code class="sourceCode cpp">cond<span class="op">&lt;</span>T, T<span class="op">&gt;</span></code> is <code class="sourceCode cpp">T</code> for all types, value categories, and const. Except one: <code class="sourceCode cpp">T <span class="kw">const</span></code>. In the strong proposal, <code class="sourceCode cpp">cond<span class="op">&lt;</span>T <span class="kw">const</span>, T <span class="kw">const</span><span class="op">&gt;</span></code> becomes <code class="sourceCode cpp">T</code>. Now, it technically is already just <code class="sourceCode cpp">T</code> for scalar types - but it’s not possible to even have a const prvalue of scalar type, so this doesn’t really matter. So we could alter the strong proposal to say that <code class="sourceCode cpp">cond<span class="op">&lt;</span>T, U<span class="op">&gt;</span></code> is only ever a const prvalue in the specific case of <code class="sourceCode cpp">cond<span class="op">&lt;</span>T <span class="kw">const</span>, T <span class="kw">const</span><span class="op">&gt;</span></code> - otherwise all prvalues are non-const. This would still technically give different answers between scalar and class types, but not in a meaningfully observed way.</p>
<h1 data-number="4" style="border-bottom:1px solid #cccccc" id="bibliography"><span class="header-section-number">4</span> References<a href="#bibliography" class="self-link"></a></h1>
<div id="refs" class="references hanging-indent" role="doc-bibliography">
<div id="ref-P2278R4">
Expand Down

0 comments on commit f428e14

Please sign in to comment.