Skip to content

Commit 89c9f03

Browse files
authored
Merge pull request #108 from iris-cpp/optimize-rvariant
Fix type-changing bugs
2 parents 0174057 + 241743a commit 89c9f03

10 files changed

Lines changed: 919 additions & 471 deletions

File tree

‎doc/rvariant.adoc‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -795,13 +795,13 @@ where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_t}
795795
+
796796
*_Returns:_* [.underline]#Let `o` denote# a reference to the new contained value. [.underline]#Returns `{unwrap_recursive}(o)`.#
797797
+
798-
*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `std::forward<Args>(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)#
798+
*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `std::forward<Args>(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. Additionally, if `*this` holds a value of a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to destroy that value after constructing such an intermediate variable, for any `T~_I_~`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)#
799799

800800
* [.candidate]#4)# Equivalent to the `std::variant` counterpart, ^https://eel.is/c++draft/variant.mod[[spec\]]^ except:
801801
+
802802
*_Returns:_* [.underline]#Let `o` denote# a reference to the new contained value. [.underline]#Returns `{unwrap_recursive}(o)`.#
803803
+
804-
*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `il, std::forward<Args>(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)#
804+
*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `il, std::forward<Args>(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. Additionally, if `*this` holds a value of a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to destroy that value after constructing such an intermediate variable, for any `T~_I_~`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)#
805805

806806
[[rvariant.status]]
807807
=== Value status [.slug]##<<rvariant.status,[rvariant.status]>>##
@@ -1451,6 +1451,29 @@ _Note 2:_ It is currently unknown whether the recursive instantiation scenario d
14511451
*_Effects:_* Equivalent to the `std::indirect` counterpart. ^link:pass:[https://eel.is/c++draft/indirect.ctor#lib:indirect,constructor______][[spec\]]^
14521452

14531453

1454+
[[rvariant.recursive.relops]]
1455+
=== Comparisons
1456+
Effectively overrides only the ones listed below; rest are the same as `std::indirect` counterparts. ^https://eel.is/c++draft/indirect.relops[[spec\]]^ ^https://eel.is/c++draft/indirect.comp.with.t[[spec\]]^
1457+
1458+
[,cpp,subs="+macros,+attributes"]
1459+
----
1460+
template<class T, class A, class U, class AA>
1461+
constexpr bool operator==(recursive_wrapper_alloca<T, A> const& lhs, recursive_wrapper_alloca<U, AA> const& rhs)
1462+
pass:quotes[[.underline\]#/* no noexcept-specifier */#];pass:quotes[[.candidate\]#// 1#]
1463+
1464+
template<class T, class A, class U>
1465+
constexpr bool operator==(recursive_wrapper_alloca<T, A> const& lhs, U const& rhs)
1466+
pass:quotes[[.underline\]#/* no noexcept-specifier */#];pass:quotes[[.candidate\]#// 2#]
1467+
----
1468+
1469+
[.candidates]
1470+
* [.candidate]#1-2)# Equivalent to the `std::indirect` counterparts, except that [.underline]#they have no _noexcept-specifier_#.
1471+
+
1472+
*_Note:_* The _noexcept-specifier_ of the `std::indirect` counterparts depends on the comparison of `T`. Since an exception specification is needed whenever the function is selected by overload resolution, such a _noexcept-specifier_ makes the comparison of a recursive type depend on itself; for example, if a class `X` has a defaulted `operator==` and contains `rvariant<..., recursive_wrapper<X>>`, checking the constraints of ``rvariant``'s `operator==` requires the comparison of `X`, which in turn requires checking the same constraints. Some compilers (e.g. GCC) reject such a program. The _noexcept-specifier_ is omitted to break this recursion.
1473+
1474+
WARNING: [.underline]#Recursive types must implement their own *non-defaulted* `operatorpass:quotes[&lt;=&gt;]`.# For a class `X` that contains `rvariant<..., recursive_wrapper<X>>`, a defaulted `operatorpass:quotes[&lt;=&gt;]` of `X` requires the result of ``rvariant``'s `operatorpass:quotes[&lt;=&gt;]`, whose constraints and return type in turn require the result of comparing `X` through `recursive_wrapper<X>`. This leads to infinite recursive instantiation, regardless of whether the return type of the defaulted `operatorpass:quotes[&lt;=&gt;]` is `auto` or a specific comparison category type. This is a limitation of the language itself; standard containers have a similar limitation (e.g. a class `X` that contains `std::vector<X>` cannot have a defaulted `operatorpass:quotes[&lt;=&gt;]` whose return type is `auto`).
1475+
1476+
14541477
[[rvariant.recursive.helper]]
14551478
== `recursive_wrapper` helper utilities [.slug]##<<rvariant.recursive.helper,[rvariant.recursive.helper]>>##
14561479

‎doc/rvariant.html‎

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,7 @@ <h1>rvariant<a href="https://github.com/iris-cpp/iris" target="_blank" rel="noop
902902
<ul class="sectlevel2">
903903
<li><a href="#rvariant.recursive.general">General <span class="slug">[rvariant.recursive.general]</span></a></li>
904904
<li><a href="#rvariant.recursive.ctor">Constructors</a></li>
905+
<li><a href="#rvariant.recursive.relops">Comparisons</a></li>
905906
</ul>
906907
</li>
907908
<li><a href="#rvariant.recursive.helper"><code>recursive_wrapper</code> helper utilities <span class="slug">[rvariant.recursive.helper]</span></a></li>
@@ -1939,7 +1940,7 @@ <h3 id="rvariant.mod"><a class="anchor" href="#rvariant.mod"></a>Modifiers <span
19391940
<p><strong><em>Returns:</em></strong> <span class="underline">Let <code>o</code> denote</span> a reference to the new contained value. <span class="underline">Returns <code><a href="#rvariant.recursive.helper">unwrap_recursive</a>(o)</code>.</span></p>
19401941
</div>
19411942
<div class="paragraph">
1942-
<p><strong><em>Remarks:</em></strong> <span class="underline">If <code>T<sub><em>I</em></sub></code> is a specialization of <code><a href="#rvariant.recursive">recursive_wrapper</a></code> or <code><a href="#rvariant.recursive">recursive_wrapper_alloca</a></code>, this function is permitted to construct an intermediate variable <code>tmp</code> as if by passing <code>std::forward&lt;Args&gt;(args)...</code> to <code>T<sub><em>I</em></sub></code>'s constructor. Then <code>rvariant</code> direct-non-list-initializes the contained value of <code>T<sub><em>I</em></sub></code> with the argument <code>std::move(tmp)</code>. (<em>Note:</em> This allows optimization where <code>rvariant</code> can be assumed to become never valueless on certain cases.)</span></p>
1943+
<p><strong><em>Remarks:</em></strong> <span class="underline">If <code>T<sub><em>I</em></sub></code> is a specialization of <code><a href="#rvariant.recursive">recursive_wrapper</a></code> or <code><a href="#rvariant.recursive">recursive_wrapper_alloca</a></code>, this function is permitted to construct an intermediate variable <code>tmp</code> as if by passing <code>std::forward&lt;Args&gt;(args)...</code> to <code>T<sub><em>I</em></sub></code>'s constructor. Then <code>rvariant</code> direct-non-list-initializes the contained value of <code>T<sub><em>I</em></sub></code> with the argument <code>std::move(tmp)</code>. Additionally, if <code>*this</code> holds a value of a specialization of <code><a href="#rvariant.recursive">recursive_wrapper</a></code> or <code><a href="#rvariant.recursive">recursive_wrapper_alloca</a></code>, this function is permitted to destroy that value after constructing such an intermediate variable, for any <code>T<sub><em>I</em></sub></code>. (<em>Note:</em> This allows optimization where <code>rvariant</code> can be assumed to become never valueless on certain cases.)</span></p>
19431944
</div>
19441945
</li>
19451946
<li>
@@ -1948,7 +1949,7 @@ <h3 id="rvariant.mod"><a class="anchor" href="#rvariant.mod"></a>Modifiers <span
19481949
<p><strong><em>Returns:</em></strong> <span class="underline">Let <code>o</code> denote</span> a reference to the new contained value. <span class="underline">Returns <code><a href="#rvariant.recursive.helper">unwrap_recursive</a>(o)</code>.</span></p>
19491950
</div>
19501951
<div class="paragraph">
1951-
<p><strong><em>Remarks:</em></strong> <span class="underline">If <code>T<sub><em>I</em></sub></code> is a specialization of <code><a href="#rvariant.recursive">recursive_wrapper</a></code> or <code><a href="#rvariant.recursive">recursive_wrapper_alloca</a></code>, this function is permitted to construct an intermediate variable <code>tmp</code> as if by passing <code>il, std::forward&lt;Args&gt;(args)...</code> to <code>T<sub><em>I</em></sub></code>'s constructor. Then <code>rvariant</code> direct-non-list-initializes the contained value of <code>T<sub><em>I</em></sub></code> with the argument <code>std::move(tmp)</code>. (<em>Note:</em> This allows optimization where <code>rvariant</code> can be assumed to become never valueless on certain cases.)</span></p>
1952+
<p><strong><em>Remarks:</em></strong> <span class="underline">If <code>T<sub><em>I</em></sub></code> is a specialization of <code><a href="#rvariant.recursive">recursive_wrapper</a></code> or <code><a href="#rvariant.recursive">recursive_wrapper_alloca</a></code>, this function is permitted to construct an intermediate variable <code>tmp</code> as if by passing <code>il, std::forward&lt;Args&gt;(args)...</code> to <code>T<sub><em>I</em></sub></code>'s constructor. Then <code>rvariant</code> direct-non-list-initializes the contained value of <code>T<sub><em>I</em></sub></code> with the argument <code>std::move(tmp)</code>. Additionally, if <code>*this</code> holds a value of a specialization of <code><a href="#rvariant.recursive">recursive_wrapper</a></code> or <code><a href="#rvariant.recursive">recursive_wrapper_alloca</a></code>, this function is permitted to destroy that value after constructing such an intermediate variable, for any <code>T<sub><em>I</em></sub></code>. (<em>Note:</em> This allows optimization where <code>rvariant</code> can be assumed to become never valueless on certain cases.)</span></p>
19521953
</div>
19531954
</li>
19541955
</ul>
@@ -2820,6 +2821,45 @@ <h3 id="rvariant.recursive.ctor"><a class="anchor" href="#rvariant.recursive.cto
28202821
</ul>
28212822
</div>
28222823
</div>
2824+
<div class="sect2">
2825+
<h3 id="rvariant.recursive.relops"><a class="anchor" href="#rvariant.recursive.relops"></a>Comparisons</h3>
2826+
<div class="paragraph">
2827+
<p>Effectively overrides only the ones listed below; rest are the same as <code>std::indirect</code> counterparts. <sup><a href="https://eel.is/cdraft/indirect.relops[[spec\]]^ ^https://eel.is/cdraft/indirect.comp.with.t">[spec]</a></sup></p>
2828+
</div>
2829+
<div class="listingblock">
2830+
<div class="content">
2831+
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">template&lt;class T, class A, class U, class AA&gt;
2832+
constexpr bool operator==(recursive_wrapper_alloca&lt;T, A&gt; const&amp; lhs, recursive_wrapper_alloca&lt;U, AA&gt; const&amp; rhs)
2833+
<span class="underline">/* no noexcept-specifier */</span>;<span class="candidate">// 1</span>
2834+
2835+
template&lt;class T, class A, class U&gt;
2836+
constexpr bool operator==(recursive_wrapper_alloca&lt;T, A&gt; const&amp; lhs, U const&amp; rhs)
2837+
<span class="underline">/* no noexcept-specifier */</span>;<span class="candidate">// 2</span></code></pre>
2838+
</div>
2839+
</div>
2840+
<div class="ulist candidates">
2841+
<ul>
2842+
<li>
2843+
<p><span class="candidate">1-2)</span> Equivalent to the <code>std::indirect</code> counterparts, except that <span class="underline">they have no <em>noexcept-specifier</em></span>.</p>
2844+
<div class="paragraph">
2845+
<p><strong><em>Note:</em></strong> The <em>noexcept-specifier</em> of the <code>std::indirect</code> counterparts depends on the comparison of <code>T</code>. Since an exception specification is needed whenever the function is selected by overload resolution, such a <em>noexcept-specifier</em> makes the comparison of a recursive type depend on itself; for example, if a class <code>X</code> has a defaulted <code>operator==</code> and contains <code>rvariant&lt;&#8230;&#8203;, recursive_wrapper&lt;X&gt;&gt;</code>, checking the constraints of <code>rvariant</code>'s <code>operator==</code> requires the comparison of <code>X</code>, which in turn requires checking the same constraints. Some compilers (e.g. GCC) reject such a program. The <em>noexcept-specifier</em> is omitted to break this recursion.</p>
2846+
</div>
2847+
</li>
2848+
</ul>
2849+
</div>
2850+
<div class="admonitionblock warning">
2851+
<table>
2852+
<tr>
2853+
<td class="icon">
2854+
<i class="fa icon-warning" title="Warning"></i>
2855+
</td>
2856+
<td class="content">
2857+
<span class="underline">Recursive types must implement their own <strong>non-defaulted</strong> <code>operator&lt;=&gt;</code>.</span> For a class <code>X</code> that contains <code>rvariant&lt;&#8230;&#8203;, recursive_wrapper&lt;X&gt;&gt;</code>, a defaulted <code>operator&lt;=&gt;</code> of <code>X</code> requires the result of <code>rvariant</code>'s <code>operator&lt;=&gt;</code>, whose constraints and return type in turn require the result of comparing <code>X</code> through <code>recursive_wrapper&lt;X&gt;</code>. This leads to infinite recursive instantiation, regardless of whether the return type of the defaulted <code>operator&lt;=&gt;</code> is <code>auto</code> or a specific comparison category type. This is a limitation of the language itself; standard containers have a similar limitation (e.g. a class <code>X</code> that contains <code>std::vector&lt;X&gt;</code> cannot have a defaulted <code>operator&lt;=&gt;</code> whose return type is <code>auto</code>).
2858+
</td>
2859+
</tr>
2860+
</table>
2861+
</div>
2862+
</div>
28232863
</div>
28242864
</div>
28252865
<div class="sect1">

0 commit comments

Comments
 (0)