forked from boostorg/mp11
Add another example
This commit is contained in:
@@ -38,6 +38,8 @@
|
||||
<dd><dl>
|
||||
<dt><span class="section"><a href="mp11.html#mp11.examples.generating_test_cases">Generating Test
|
||||
Cases</a></span></dt>
|
||||
<dt><span class="section"><a href="mp11.html#mp11.examples.writing_common_type_specializati">Writing
|
||||
<code class="computeroutput"><span class="identifier">common_type</span></code> specializations</a></span></dt>
|
||||
<dt><span class="section"><a href="mp11.html#mp11.examples.fixing_tuple_cat">Fixing <code class="computeroutput"><span class="identifier">tuple_cat</span></code></a></span></dt>
|
||||
<dt><span class="section"><a href="mp11.html#mp11.examples.computing_return_types">Computing Return
|
||||
Types</a></span></dt>
|
||||
@@ -401,6 +403,102 @@
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="mp11.examples.writing_common_type_specializati"></a><a class="link" href="mp11.html#mp11.examples.writing_common_type_specializati" title="Writing common_type specializations">Writing
|
||||
<code class="computeroutput"><span class="identifier">common_type</span></code> specializations</a>
|
||||
</h3></div></div></div>
|
||||
<p>
|
||||
The standard trait <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type</span></code>, used to obtain a type to which
|
||||
all of its arguments can convert without unnecessary loss of precision, can
|
||||
be user-specialized when its default implementation (based on the ternary
|
||||
<code class="computeroutput"><span class="special">?:</span></code> operator) is unsuitable.
|
||||
</p>
|
||||
<p>
|
||||
Let's write a <code class="computeroutput"><span class="identifier">common_type</span></code>
|
||||
specialization for two <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span></code>
|
||||
arguments. For that, we need a metafunction that applies <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type</span></code>
|
||||
to each pair of elements and gathers the results into a tuple:
|
||||
</p>
|
||||
<pre class="programlisting"><span class="keyword">template</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">Tp1</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Tp2</span><span class="special">></span> <span class="keyword">using</span> <span class="identifier">common_tuple</span> <span class="special">=</span> <span class="identifier">mp_transform</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type_t</span><span class="special">,</span> <span class="identifier">Tp1</span><span class="special">,</span> <span class="identifier">Tp2</span><span class="special">>;</span>
|
||||
</pre>
|
||||
<p>
|
||||
then specialize <code class="computeroutput"><span class="identifier">common_type</span></code>
|
||||
to use it:
|
||||
</p>
|
||||
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">std</span>
|
||||
<span class="special">{</span>
|
||||
|
||||
<span class="keyword">template</span><span class="special"><</span><span class="keyword">class</span><span class="special">...</span> <span class="identifier">T1</span><span class="special">,</span> <span class="keyword">class</span><span class="special">...</span> <span class="identifier">T2</span><span class="special">></span> <span class="keyword">struct</span> <span class="identifier">common_type</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special"><</span><span class="identifier">T1</span><span class="special">...>,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special"><</span><span class="identifier">T2</span><span class="special">...>>:</span> <span class="identifier">mp_defer</span><span class="special"><</span><span class="identifier">common_tuple</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special"><</span><span class="identifier">T1</span><span class="special">...>,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special"><</span><span class="identifier">T2</span><span class="special">...>></span>
|
||||
<span class="special">{</span>
|
||||
<span class="special">};</span>
|
||||
|
||||
<span class="special">}</span> <span class="comment">// std</span>
|
||||
</pre>
|
||||
<p>
|
||||
(There is no need to specialize <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type</span></code>
|
||||
for more than two arguments - it takes care of synthesizing the appropriate
|
||||
semantics from the binary case.)
|
||||
</p>
|
||||
<p>
|
||||
The subtlety here is the use of <code class="computeroutput"><span class="identifier">mp_defer</span></code>.
|
||||
We could have defined a nested <code class="computeroutput"><span class="identifier">type</span></code>
|
||||
to <code class="computeroutput"><span class="identifier">common_tuple</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special"><</span><span class="identifier">T1</span><span class="special">...>,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special"><</span><span class="identifier">T2</span><span class="special">...>></span></code>, and it would still have worked
|
||||
in all valid cases. By letting <code class="computeroutput"><span class="identifier">mp_defer</span></code>
|
||||
define <code class="computeroutput"><span class="identifier">type</span></code>, though, we make
|
||||
our specialization <span class="emphasis"><em>SFINAE-friendly</em></span>.
|
||||
</p>
|
||||
<p>
|
||||
That is, when our <code class="computeroutput"><span class="identifier">common_tuple</span></code>
|
||||
causes a substitution failure instead of a hard error, <code class="computeroutput"><span class="identifier">mp_defer</span></code>
|
||||
will not define a nested <code class="computeroutput"><span class="identifier">type</span></code>,
|
||||
and <code class="computeroutput"><span class="identifier">common_type_t</span></code>, which
|
||||
is defined as <code class="computeroutput"><span class="keyword">typename</span> <span class="identifier">common_type</span><span class="special"><...>::</span><span class="identifier">type</span></code>,
|
||||
will also cause a substitution failure.
|
||||
</p>
|
||||
<p>
|
||||
As another example, consider the hypothetical type <code class="computeroutput"><span class="identifier">expected</span><span class="special"><</span><span class="identifier">T</span><span class="special">,</span>
|
||||
<span class="identifier">E</span><span class="special">...></span></code>
|
||||
that represents either a successful return with a value of <code class="computeroutput"><span class="identifier">T</span></code>, or an unsucessful return with an error
|
||||
code of some type in the list <code class="computeroutput"><span class="identifier">E</span><span class="special">...</span></code>. The common type of <code class="computeroutput"><span class="identifier">expected</span><span class="special"><</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">E1</span><span class="special">,</span>
|
||||
<span class="identifier">E2</span><span class="special">,</span> <span class="identifier">E3</span><span class="special">></span></code>
|
||||
and <code class="computeroutput"><span class="identifier">expected</span><span class="special"><</span><span class="identifier">T2</span><span class="special">,</span> <span class="identifier">E1</span><span class="special">,</span> <span class="identifier">E4</span><span class="special">,</span>
|
||||
<span class="identifier">E5</span><span class="special">></span></code>
|
||||
is <code class="computeroutput"><span class="identifier">expected</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type_t</span><span class="special"><</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">T2</span><span class="special">>,</span>
|
||||
<span class="identifier">E1</span><span class="special">,</span> <span class="identifier">E2</span><span class="special">,</span> <span class="identifier">E3</span><span class="special">,</span> <span class="identifier">E4</span><span class="special">,</span>
|
||||
<span class="identifier">E5</span><span class="special">></span></code>.
|
||||
That is, the possible return values are combined into their common type,
|
||||
and we take the union of the set of error types.
|
||||
</p>
|
||||
<p>
|
||||
Therefore,
|
||||
</p>
|
||||
<pre class="programlisting"><span class="keyword">template</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">T1</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">E1</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">T2</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">E2</span><span class="special">></span> <span class="keyword">using</span> <span class="identifier">common_expected</span> <span class="special">=</span> <span class="identifier">mp_rename</span><span class="special"><</span><span class="identifier">mp_push_front</span><span class="special"><</span><span class="identifier">mp_unique</span><span class="special"><</span><span class="identifier">mp_append</span><span class="special"><</span><span class="identifier">E1</span><span class="special">,</span> <span class="identifier">E2</span><span class="special">>>,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type_t</span><span class="special"><</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">T2</span><span class="special">>>,</span> <span class="identifier">expected</span><span class="special">>;</span>
|
||||
|
||||
<span class="keyword">namespace</span> <span class="identifier">std</span>
|
||||
<span class="special">{</span>
|
||||
|
||||
<span class="keyword">template</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">T1</span><span class="special">,</span> <span class="keyword">class</span><span class="special">...</span> <span class="identifier">E1</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">T2</span><span class="special">,</span> <span class="keyword">class</span><span class="special">...</span> <span class="identifier">E2</span><span class="special">></span> <span class="keyword">struct</span> <span class="identifier">common_type</span><span class="special"><</span><span class="identifier">expected</span><span class="special"><</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">E1</span><span class="special">...>,</span> <span class="identifier">expected</span><span class="special"><</span><span class="identifier">T2</span><span class="special">,</span> <span class="identifier">E2</span><span class="special">...>>:</span> <span class="identifier">mp_defer</span><span class="special"><</span><span class="identifier">common_expected</span><span class="special">,</span> <span class="identifier">T1</span><span class="special">,</span> <span class="identifier">mp_list</span><span class="special"><</span><span class="identifier">E1</span><span class="special">...>,</span> <span class="identifier">T2</span><span class="special">,</span> <span class="identifier">mp_list</span><span class="special"><</span><span class="identifier">E2</span><span class="special">...>></span>
|
||||
<span class="special">{</span>
|
||||
<span class="special">};</span>
|
||||
|
||||
<span class="special">}</span> <span class="comment">// std</span>
|
||||
</pre>
|
||||
<p>
|
||||
Here we've taken a different tack; instead of passing the <code class="computeroutput"><span class="identifier">expected</span></code>
|
||||
types to <code class="computeroutput"><span class="identifier">common_expected</span></code>,
|
||||
we're passing the <code class="computeroutput"><span class="identifier">T</span></code> types
|
||||
and lists of the <code class="computeroutput"><span class="identifier">E</span></code> types.
|
||||
This makes our job easier. <code class="computeroutput"><span class="identifier">mp_unique</span><span class="special"><</span><span class="identifier">mp_append</span><span class="special"><</span><span class="identifier">E1</span><span class="special">,</span> <span class="identifier">E2</span><span class="special">>></span></code>
|
||||
gives us the concatenation of <code class="computeroutput"><span class="identifier">E1</span></code>
|
||||
and <code class="computeroutput"><span class="identifier">E2</span></code> with the duplicates
|
||||
removed; we then add <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type_t</span><span class="special"><</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">T2</span><span class="special">></span></code>
|
||||
to the front via <code class="computeroutput"><span class="identifier">mp_push_front</span></code>;
|
||||
and finally, we <code class="computeroutput"><span class="identifier">mp_rename</span></code>
|
||||
the resultant <code class="computeroutput"><span class="identifier">mp_list</span></code> to
|
||||
<code class="computeroutput"><span class="identifier">expected</span></code>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="mp11.examples.fixing_tuple_cat"></a><a class="link" href="mp11.html#mp11.examples.fixing_tuple_cat" title="Fixing tuple_cat">Fixing <code class="computeroutput"><span class="identifier">tuple_cat</span></code></a>
|
||||
</h3></div></div></div>
|
||||
<p>
|
||||
@@ -2317,7 +2415,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
|
||||
<td align="left"><p><small>Last revised: May 18, 2017 at 22:41:31 GMT</small></p></td>
|
||||
<td align="left"><p><small>Last revised: May 18, 2017 at 23:36:45 GMT</small></p></td>
|
||||
<td align="right"><div class="copyright-footer"></div></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
|
@@ -87,6 +87,62 @@ tuple element; we use a (C++14) lambda that calls `test_result`. (In pure C++11,
|
||||
function object with a templated `operator()` and pass that to `tuple_for_each` directly.)
|
||||
[endsect]
|
||||
|
||||
[section Writing `common_type` specializations]
|
||||
|
||||
The standard trait `std::common_type`, used to obtain a type to which all of its arguments can convert without
|
||||
unnecessary loss of precision, can be user-specialized when its default implementation (based on the ternary `?:`
|
||||
operator) is unsuitable.
|
||||
|
||||
Let's write a `common_type` specialization for two `std::tuple` arguments. For that, we need a metafunction that
|
||||
applies `std::common_type` to each pair of elements and gathers the results into a tuple:
|
||||
|
||||
template<class Tp1, class Tp2> using common_tuple = mp_transform<std::common_type_t, Tp1, Tp2>;
|
||||
|
||||
then specialize `common_type` to use it:
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<class... T1, class... T2> struct common_type<std::tuple<T1...>, std::tuple<T2...>>: mp_defer<common_tuple, std::tuple<T1...>, std::tuple<T2...>>
|
||||
{
|
||||
};
|
||||
|
||||
} // std
|
||||
|
||||
(There is no need to specialize `std::common_type` for more than two arguments - it takes care of synthesizing the appropriate semantics from
|
||||
the binary case.)
|
||||
|
||||
The subtlety here is the use of `mp_defer`. We could have defined a nested `type` to `common_tuple<std::tuple<T1...>, std::tuple<T2...>>`,
|
||||
and it would still have worked in all valid cases. By letting `mp_defer` define `type`, though, we make our specialization /SFINAE-friendly/.
|
||||
|
||||
That is, when our `common_tuple` causes a substitution failure instead of a hard error, `mp_defer` will not define a nested `type`,
|
||||
and `common_type_t`, which is defined as `typename common_type<...>::type`, will also cause a substitution failure.
|
||||
|
||||
As another example, consider the hypothetical type `expected<T, E...>` that represents either a successful return with a value of `T`,
|
||||
or an unsucessful return with an error code of some type in the list `E...`. The common type of `expected<T1, E1, E2, E3>` and
|
||||
`expected<T2, E1, E4, E5>` is `expected<std::common_type_t<T1, T2>, E1, E2, E3, E4, E5>`. That is, the possible return values are
|
||||
combined into their common type, and we take the union of the set of error types.
|
||||
|
||||
Therefore,
|
||||
|
||||
template<class T1, class E1, class T2, class E2> using common_expected = mp_rename<mp_push_front<mp_unique<mp_append<E1, E2>>, std::common_type_t<T1, T2>>, expected>;
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<class T1, class... E1, class T2, class... E2> struct common_type<expected<T1, E1...>, expected<T2, E2...>>: mp_defer<common_expected, T1, mp_list<E1...>, T2, mp_list<E2...>>
|
||||
{
|
||||
};
|
||||
|
||||
} // std
|
||||
|
||||
Here we've taken a different tack; instead of passing the `expected` types to `common_expected`, we're passing the `T` types and lists of
|
||||
the `E` types. This makes our job easier. `mp_unique<mp_append<E1, E2>>` gives us the concatenation of `E1` and `E2` with the duplicates
|
||||
removed; we then add `std::common_type_t<T1, T2>` to the front via `mp_push_front`; and finally, we `mp_rename` the resultant `mp_list`
|
||||
to `expected`.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Fixing `tuple_cat`]
|
||||
|
||||
The article [@http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html Simple C++11 metaprogramming] builds an
|
||||
|
Reference in New Issue
Block a user