From 47ee95b1f2acd42ff2c1828cb68c033bb327bf65 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Wed, 17 May 2017 20:47:25 +0300 Subject: [PATCH] Add more examples --- doc/html/mp11.html | 350 +++++++++++++++++++++++++++++++++++++++++- doc/mp11/examples.qbk | 180 ++++++++++++++++++++++ doc/mp11/list.qbk | 95 ++++++++++++ 3 files changed, 624 insertions(+), 1 deletion(-) diff --git a/doc/html/mp11.html b/doc/html/mp11.html index b5bbe7b..64d4482 100644 --- a/doc/html/mp11.html +++ b/doc/html/mp11.html @@ -38,6 +38,7 @@
Generating Test Cases
+
Fixing tuple_cat
Computing Return Types
@@ -366,6 +367,258 @@ boost::tuple_for_each( mp_product<mp_list, L, L>(), [](auto&& x){ test_result(x); } ); } +

+ How does it work? +

+

+ mp_product<F, L1, L2> + calls F<T1, T2> where T1 + varies over the elements of L1 + and T2 varies over the elements + of L2, as if by executing + two nested loops. It then returns a list of these results, of the same type + as L1. +

+

+ In our case, both lists are the same std::tuple, + and F is mp_list, + so mp_product<mp_list, L, L> will get us std::tuple<mp_list<char, + char>, + mp_list<char, short>, mp_list<char, + int>, + ..., mp_list<unsigned long, long>, mp_list<unsigned long, unsigned + long>>. +

+

+ We then default-construct this tuple and pass it to tuple_for_each. + tuple_for_each(tp, f) calls f + for every tuple element; we use a (C++14) lambda that calls test_result. (In pure C++11, we'd need + to make test_result a function + object with a templated operator() and pass that to tuple_for_each + directly.) +

+ +
+ +

+ The article Simple + C++11 metaprogramming builds an implementation of the standard function + tuple_cat, with the end result + given below: +

+
template<class L> using F = mp_iota<mp_size<L>>;
+
+template<class R, class...Is, class... Ks, class Tp>
+R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
+{
+    return R{ std::get<Ks::value>(std::get<Is::value>(tp))... };
+}
+
+template<class... Tp,
+    class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
+    R tuple_cat( Tp &&... tp )
+{
+    std::size_t const N = sizeof...(Tp);
+
+    // inner
+
+    using list1 = mp_list<mp_rename<typename std::remove_reference<Tp>::type, mp_list>...>;
+    using list2 = mp_iota_c<N>;
+
+    using list3 = mp_transform<mp_fill, list1, list2>;
+
+    using inner = mp_apply<mp_append, list3>;
+
+    // outer
+
+    using list4 = mp_transform<F, list1>;
+
+    using outer = mp_apply<mp_append, list4>;
+
+    //
+
+    return tuple_cat_<R>( inner(), outer(), std::forward_as_tuple( std::forward<Tp>(tp)... ) );
+}
+
+

+ This function, however, is not entirely correct, in that it doesn't handle + some cases properly. For example, trying to concatenate tuples containing + move-only elements such as unique_ptr + fails: +

+
std::tuple<std::unique_ptr<int>> t1;
+std::tuple<std::unique_ptr<float>> t2;
+
+auto result = ::tuple_cat( std::move( t1 ), std::move( t2 ) );
+
+

+ Trying to concatenate const + tuples fails: +

+
std::tuple<int> const t1;
+std::tuple<float> const t2;
+
+auto result = ::tuple_cat( t1, t2 );
+
+

+ And finally, the standard tuple_cat + is specified to work on arbitrary tuple-like types (that is, all types that + support tuple_size, tuple_element, and get), + while our implementation only works with tuple + and pair. std::array, + for example, fails: +

+
std::array<int, 2> t1{ 1, 2 };
+std::array<float, 3> t2{ 3.0f, 4.0f, 5.0f };
+
+auto result = ::tuple_cat( t1, std::move( t2 ) );
+
+

+ Let's fix these one by one. Support for move-only types is easy, if one knows + where to look. The problem is that Tp + that we're passing to the helper tuple_cat_ + is (correctly) tuple<unique_ptr<int>&&, + unique_ptr<float>&&>, + but std::get<0>(tp) still returns unique_ptr<int>&, + because tp is an lvalue. + This behavior is a bit surprising, but consistent with how rvalue reference + members are treated by the language. +

+

+ Long story short, we need std::move(tp) + in tuple_cat_ to make tp an rvalue: +

+
template<class R, class...Is, class... Ks, class Tp>
+R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
+{
+    return R{ std::get<Ks::value>(std::get<Is::value>(std::move(tp)))... };
+}
+
+

+ Next, const-qualified tuples. + The issue here is that we're stripping references from the input tuples, + but not const. As a result, + we're trying to manipulate types such as tuple<int> + const with Mp11 algorithms, and these + types do not fit the list concept. We just need to strip qualifiers as well, + by defining the useful remove_cv_ref + primitive that is inexplicably missing from the standard library: +

+
template<class T> using remove_cv_ref = typename std::remove_cv<
+    typename std::remove_reference<T>::type>::type;
+
+

+ and then by using remove_cv_ref<Tp> in place of typename + std::remove_reference<Tp>::type: +

+
template<class... Tp,
+    class R = mp_append<std::tuple<>, remove_cv_ref<Tp>...>>
+    R tuple_cat( Tp &&... tp )
+{
+    std::size_t const N = sizeof...(Tp);
+
+    // inner
+
+    using list1 = mp_list<mp_rename<remove_cv_ref<Tp>, mp_list>...>;
+
+    // ...
+
+

+ Finally, tuple-like types. We've so far exploited the fact that std::pair + and std::tuple are valid Mp11 lists, but in general, + arbitrary tuple-like types aren't, so we need to convert them into such. + For that, we'll need to define a metafunction from_tuple_like + that will take an arbitrary tuple-like type and will return, in our case, + the corresponding mp_list. +

+

+ Technically, a more principled approach would've been to return std::tuple, + but here mp_list will prove + more convenient. +

+

+ What we need is, given a tuple-like type Tp, + to obtain mp_list<std::tuple_element<0, + Tp>, + std::tuple_element<1, Tp>, ..., std::tuple_element<N-1, Tp>>, where N + is tuple_size<Tp>::value. Here's one way to do it: +

+
template<class T, class I> using tuple_element = std::tuple_element_t<I::value, T>;
+template<class T> using from_tuple_like = mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>;
+
+

+ (mp_iota<N> is + an algorithm that returns an mp_list + with elements mp_size_t<0>, mp_size_t<1>, ..., + mp_size_t<N-1>.) +

+

+ Remember that mp_product<F, + L1, L2> + performs the equivalent of two nested loops over the elements of L1 and L2, + applying F to the two variables + and gathering the result. In our case L1 + consists of the single element T, + so only the second loop (over mp_iota<N>, + where N is tuple_size<T>), + remains, and we get a list of the same type as L1 + (an mp_list) with contents + tuple_element<T, mp_size_t<0>>, + tuple_element<T, mp_size_t<1>>, + ..., tuple_element<T, mp_size_t<N-1>>. +

+

+ For completeness's sake, here's another, more traditional way to achieve + the same result: +

+
template<class T> using from_tuple_like = mp_transform_q<mp_bind_front<tuple_element, T>, mp_iota<std::tuple_size<T>>>;
+
+

+ With all these fixes applied, our fully operational tuple_cat + now looks like this: +

+
template<class L> using F = mp_iota<mp_size<L>>;
+
+template<class R, class...Is, class... Ks, class Tp>
+R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
+{
+    return R{ std::get<Ks::value>(std::get<Is::value>(std::move(tp)))... };
+}
+
+template<class T> using remove_cv_ref = typename std::remove_cv<
+    typename std::remove_reference<T>::type>::type;
+
+template<class T, class I> using tuple_element = std::tuple_element_t<I::value, T>;
+template<class T> using from_tuple_like = mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>;
+
+template<class... Tp,
+    class R = mp_append<std::tuple<>, from_tuple_like<remove_cv_ref<Tp>>...>>
+    R tuple_cat( Tp &&... tp )
+{
+    std::size_t const N = sizeof...(Tp);
+
+    // inner
+
+    using list1 = mp_list<from_tuple_like<remove_cv_ref<Tp>>...>;
+    using list2 = mp_iota_c<N>;
+
+    using list3 = mp_transform<mp_fill, list1, list2>;
+
+    using inner = mp_apply<mp_append, list3>;
+
+    // outer
+
+    using list4 = mp_transform<F, list1>;
+
+    using outer = mp_apply<mp_append, list4>;
+
+    //
+
+    return tuple_cat_<R>( inner(), outer(), std::forward_as_tuple( std::forward<Tp>(tp)... ) );
+}
+

@@ -611,6 +864,15 @@ words, mp_size<L<T...>> is an alias for mp_size_t<sizeof...(T)>.

+
using L1 = mp_list<>;
+using R1 = mp_size<L1>; // mp_size_t<0>
+
+using L2 = std::pair<int, int>;
+using R2 = mp_size<L2>; // mp_size_t<2>
+
+using L3 = std::tuple<float>;
+using R3 = mp_size<L3>; // mp_size_t<1>
+

@@ -637,6 +899,15 @@ That is, mp_front<L<T1, T...>> is an alias for T1.

+
using L1 = std::pair<int, float>;
+using R1 = mp_front<L1>; // int
+
+using L2 = std::tuple<float, double, long double>;
+using R2 = mp_front<L2>; // float
+
+using L3 = mp_list<char[1], char[2], char[3], char[4]>;
+using R3 = mp_front<L3>; // char[1]
+

@@ -650,6 +921,12 @@ That is, mp_pop_front<L<T1, T...>> is an alias for L<T...>.

+
using L1 = std::tuple<float, double, long double>;
+using R1 = mp_pop_front<L1>; // std::tuple<double, long double>
+
+using L2 = mp_list<void>;
+using R2 = mp_pop_front<L2>; // mp_list<>
+

@@ -685,6 +962,15 @@ That is, mp_second<L<T1, T2, T...>> is an alias for T2.

+
using L1 = std::pair<int, float>;
+using R1 = mp_second<L1>; // float
+
+using L2 = std::tuple<float, double, long double>;
+using R2 = mp_second<L2>; // double
+
+using L3 = mp_list<char[1], char[2], char[3], char[4]>;
+using R3 = mp_second<L3>; // char[2]
+

@@ -698,6 +984,12 @@ That is, mp_third<L<T1, T2, T3, T...>> is an alias for T3.

+
using L1 = std::tuple<float, double, long double>;
+using R1 = mp_third<L1>; // long double
+
+using L2 = mp_list<char[1], char[2], char[3], char[4]>;
+using R2 = mp_third<L2>; // char[3]
+

@@ -710,6 +1002,12 @@ at the front of the list L. That is, mp_push_front<L<U...>, T...> is an alias for L<T..., U...>.

+
using L1 = std::tuple<double, long double>;
+using R1 = mp_push_front<L1, float>; // std::tuple<float, double, long double>
+
+using L2 = mp_list<void>;
+using R2 = mp_push_front<L2, char[1], char[2]>; // mp_list<char[1], char[2], void>
+

@@ -723,6 +1021,12 @@ That is, mp_push_back<L<U...>, T...> is an alias for L<U..., T...>.

+
using L1 = std::tuple<double, long double>;
+using R1 = mp_push_back<L1, float>; // std::tuple<double, long double, float>
+
+using L2 = mp_list<void>;
+using R2 = mp_push_back<L2, char[1], char[2]>; // mp_list<void, char[1], char[2]>
+

@@ -736,6 +1040,12 @@ That is, mp_rename<L<T...>, Y> is an alias for Y<T...>.

+
using L1 = std::pair<double, long double>;
+using R1 = mp_rename<L1, std::tuple>; // std::tuple<double, long double>
+
+using L2 = std::tuple<void>;
+using R2 = mp_rename<L2, mp_list>; // mp_list<void>
+

@@ -751,6 +1061,9 @@ mp_rename with the arguments reversed.)

+
using L1 = std::pair<double, long double>;
+using R1 = mp_apply<std::is_same, L1>; // std::is_same<double, long double>
+

@@ -763,6 +1076,10 @@ Same as mp_apply, but takes a quoted metafunction.

+
using L1 = std::tuple<double, long double>;
+using L2 = mp_list<int, long>;
+using R1 = mp_apply_q<mp_bind_front<mp_push_back, L1>, L2>; // std::tuple<double, long double, int, long>
+

@@ -777,6 +1094,13 @@ Ln<Tn...>> is an alias for L1<T1..., T2..., ..., Tn...>.

+
using L1 = std::tuple<double, long double>;
+using L2 = mp_list<int>;
+using L3 = std::pair<short, long>;
+using L4 = mp_list<>;
+
+using R1 = mp_append<L1, L2, L3, L4>; // std::tuple<double, long double, int, short, long>
+

@@ -791,6 +1115,15 @@ T> is an alias for L<T, U...>.

+
using L1 = std::pair<int, float>;
+using R1 = mp_replace_front<L1, void>; // std::pair<void, float>
+
+using L2 = std::tuple<float, double, long double>;
+using R2 = mp_replace_front<L2, void>; // std::tuple<void, double, long double>
+
+using L3 = mp_list<char[1], char[2], char[3], char[4]>;
+using R3 = mp_replace_front<L3, void>; // mp_list<void, char[2], char[3], char[4]>;
+

@@ -815,6 +1148,15 @@ T> is an alias for L<U1, T, U...>.

+
using L1 = std::pair<int, float>;
+using R1 = mp_replace_second<L1, void>; // std::pair<int, void>
+
+using L2 = std::tuple<float, double, long double>;
+using R2 = mp_replace_second<L2, void>; // std::tuple<float, void, long double>
+
+using L3 = mp_list<char[1], char[2], char[3], char[4]>;
+using R3 = mp_replace_second<L3, void>; // mp_list<char[1], void, char[3], char[4]>;
+

@@ -829,6 +1171,12 @@ T> is an alias for L<U1, U2, T, U...>.

+
using L1 = std::tuple<float, double, long double>;
+using R1 = mp_replace_third<L1, void>; // std::tuple<float, double, void>
+
+using L2 = mp_list<char[1], char[2], char[3], char[4]>;
+using R2 = mp_replace_third<L2, void>; // mp_list<char[1], char[2], void, char[4]>;
+

@@ -1951,7 +2299,7 @@
- +

Last revised: May 13, 2017 at 18:21:04 GMT

Last revised: May 17, 2017 at 17:44:23 GMT


diff --git a/doc/mp11/examples.qbk b/doc/mp11/examples.qbk index 9e5a7de..9d85678 100644 --- a/doc/mp11/examples.qbk +++ b/doc/mp11/examples.qbk @@ -73,6 +73,186 @@ how we can leverage Mp11 to automate the task: boost::tuple_for_each( mp_product(), [](auto&& x){ test_result(x); } ); } +How does it work? + +`mp_product` calls `F` where `T1` varies over the elements of `L1` and `T2` varies over +the elements of `L2`, as if by executing two nested loops. It then returns a list of these results, of the same +type as `L1`. + +In our case, both lists are the same `std::tuple`, and `F` is `mp_list`, so `mp_product` will get us +`std::tuple, mp_list, mp_list, ..., mp_list, mp_list>`. + +We then default-construct this tuple and pass it to `tuple_for_each`. `tuple_for_each(tp, f)` calls `f` for every +tuple element; we use a (C++14) lambda that calls `test_result`. (In pure C++11, we'd need to make `test_result` a +function object with a templated `operator()` and pass that to `tuple_for_each` directly.) +[endsect] + +[section Fixing `tuple_cat`] + +The article [@http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html Simple C++11 metaprogramming] builds an +implementation of the standard function `tuple_cat`, with the end result given below: + + template using F = mp_iota>; + + template + R tuple_cat_( mp_list, mp_list, Tp tp ) + { + return R{ std::get(std::get(tp))... }; + } + + template, typename std::remove_reference::type...>> + R tuple_cat( Tp &&... tp ) + { + std::size_t const N = sizeof...(Tp); + + // inner + + using list1 = mp_list::type, mp_list>...>; + using list2 = mp_iota_c; + + using list3 = mp_transform; + + using inner = mp_apply; + + // outer + + using list4 = mp_transform; + + using outer = mp_apply; + + // + + return tuple_cat_( inner(), outer(), std::forward_as_tuple( std::forward(tp)... ) ); + } + +This function, however, is not entirely correct, in that it doesn't handle some cases properly. For example, +trying to concatenate tuples containing move-only elements such as `unique_ptr` fails: + + std::tuple> t1; + std::tuple> t2; + + auto result = ::tuple_cat( std::move( t1 ), std::move( t2 ) ); + +Trying to concatenate `const` tuples fails: + + std::tuple const t1; + std::tuple const t2; + + auto result = ::tuple_cat( t1, t2 ); + +And finally, the standard `tuple_cat` is specified to work on arbitrary tuple-like types (that is, all types +that support `tuple_size`, `tuple_element`, and `get`), while our implementation only works with `tuple` and +`pair`. `std::array`, for example, fails: + + std::array t1{ 1, 2 }; + std::array t2{ 3.0f, 4.0f, 5.0f }; + + auto result = ::tuple_cat( t1, std::move( t2 ) ); + +Let's fix these one by one. Support for move-only types is easy, if one knows where to look. The problem is +that `Tp` that we're passing to the helper `tuple_cat_` is (correctly) `tuple&&, unique_ptr&&>`, +but `std::get<0>(tp)` still returns `unique_ptr&`, because `tp` is an lvalue. This behavior is a bit +surprising, but consistent with how rvalue reference members are treated by the language. + +Long story short, we need `std::move(tp)` in `tuple_cat_` to make `tp` an rvalue: + + template + R tuple_cat_( mp_list, mp_list, Tp tp ) + { + return R{ std::get(std::get(std::move(tp)))... }; + } + +Next, `const`-qualified tuples. The issue here is that we're stripping references from the input tuples, but not +`const`. As a result, we're trying to manipulate types such as `tuple const` with Mp11 algorithms, and these +types do not fit the list concept. We just need to strip qualifiers as well, by defining the useful `remove_cv_ref` +primitive that is inexplicably missing from the standard library: + + template using remove_cv_ref = typename std::remove_cv< + typename std::remove_reference::type>::type; + +and then by using `remove_cv_ref` in place of `typename std::remove_reference::type`: + + template, remove_cv_ref...>> + R tuple_cat( Tp &&... tp ) + { + std::size_t const N = sizeof...(Tp); + + // inner + + using list1 = mp_list, mp_list>...>; + + // ... + +Finally, tuple-like types. We've so far exploited the fact that `std::pair` and `std::tuple` are valid Mp11 lists, +but in general, arbitrary tuple-like types aren't, so we need to convert them into such. For that, we'll need to +define a metafunction `from_tuple_like` that will take an arbitrary tuple-like type and will return, in our case, +the corresponding `mp_list`. + +Technically, a more principled approach would've been to return `std::tuple`, but here `mp_list` will prove more +convenient. + +What we need is, given a tuple-like type `Tp`, to obtain `mp_list, std::tuple_element<1, Tp>, +..., std::tuple_element>`, where `N` is `tuple_size::value`. Here's one way to do it: + + template using tuple_element = std::tuple_element_t; + template using from_tuple_like = mp_product, mp_iota>>; + +(`mp_iota` is an algorithm that returns an `mp_list` with elements `mp_size_t<0>`, `mp_size_t<1>`, ..., `mp_size_t`.) + +Remember that `mp_product` performs the equivalent of two nested loops over the elements of `L1` and `L2`, +applying `F` to the two variables and gathering the result. In our case `L1` consists of the single element `T`, so +only the second loop (over `mp_iota`, where `N` is `tuple_size`), remains, and we get a list of the same type +as `L1` (an `mp_list`) with contents `tuple_element>`, `tuple_element>`, ..., +`tuple_element>`. + +For completeness's sake, here's another, more traditional way to achieve the same result: + + template using from_tuple_like = mp_transform_q, mp_iota>>; + +With all these fixes applied, our fully operational `tuple_cat` now looks like this: + + template using F = mp_iota>; + + template + R tuple_cat_( mp_list, mp_list, Tp tp ) + { + return R{ std::get(std::get(std::move(tp)))... }; + } + + template using remove_cv_ref = typename std::remove_cv< + typename std::remove_reference::type>::type; + + template using tuple_element = std::tuple_element_t; + template using from_tuple_like = mp_product, mp_iota>>; + + template, from_tuple_like>...>> + R tuple_cat( Tp &&... tp ) + { + std::size_t const N = sizeof...(Tp); + + // inner + + using list1 = mp_list>...>; + using list2 = mp_iota_c; + + using list3 = mp_transform; + + using inner = mp_apply; + + // outer + + using list4 = mp_transform; + + using outer = mp_apply; + + // + + return tuple_cat_( inner(), outer(), std::forward_as_tuple( std::forward(tp)... ) ); + } + [endsect] [section Computing Return Types] diff --git a/doc/mp11/list.qbk b/doc/mp11/list.qbk index ccddc86..6f96665 100644 --- a/doc/mp11/list.qbk +++ b/doc/mp11/list.qbk @@ -21,6 +21,15 @@ the list. `mp_size` returns the number of elements in the list `L`, as a `mp_size_t`. In other words, `mp_size>` is an alias for `mp_size_t`. + + using L1 = mp_list<>; + using R1 = mp_size; // mp_size_t<0> + + using L2 = std::pair; + using R2 = mp_size; // mp_size_t<2> + + using L3 = std::tuple; + using R3 = mp_size; // mp_size_t<1> [endsect] [section `mp_empty`] @@ -33,12 +42,27 @@ the list. template using mp_front = /*...*/; `mp_front` is the first element of the list `L`. That is, `mp_front>` is an alias for `T1`. + + using L1 = std::pair; + using R1 = mp_front; // int + + using L2 = std::tuple; + using R2 = mp_front; // float + + using L3 = mp_list; + using R3 = mp_front; // char[1] [endsect] [section `mp_pop_front`] template using mp_pop_front = /*...*/; `mp_pop_front` removes the first element of the list `L`. That is, `mp_pop_front>` is an alias for `L`. + + using L1 = std::tuple; + using R1 = mp_pop_front; // std::tuple + + using L2 = mp_list; + using R2 = mp_pop_front; // mp_list<> [endsect] [section `mp_first`] @@ -57,12 +81,27 @@ the list. template using mp_second = /*...*/; `mp_second` is the second element of the list `L`. That is, `mp_second>` is an alias for `T2`. + + using L1 = std::pair; + using R1 = mp_second; // float + + using L2 = std::tuple; + using R2 = mp_second; // double + + using L3 = mp_list; + using R3 = mp_second; // char[2] [endsect] [section `mp_third`] template using mp_third = /*...*/; `mp_third` is the third element of the list `L`. That is, `mp_third>` is an alias for `T3`. + + using L1 = std::tuple; + using R1 = mp_third; // long double + + using L2 = mp_list; + using R2 = mp_third; // char[3] [endsect] [section `mp_push_front`] @@ -70,6 +109,12 @@ the list. `mp_push_front` inserts the elements `T...` at the front of the list `L`. That is, `mp_push_front, T...>` is an alias for `L`. + + using L1 = std::tuple; + using R1 = mp_push_front; // std::tuple + + using L2 = mp_list; + using R2 = mp_push_front; // mp_list [endsect] [section `mp_push_back`] @@ -77,12 +122,24 @@ is an alias for `L`. `mp_push_back` inserts the elements `T...` at the back of the list `L`. That is, `mp_push_back, T...>` is an alias for `L`. + + using L1 = std::tuple; + using R1 = mp_push_back; // std::tuple + + using L2 = mp_list; + using R2 = mp_push_back; // mp_list [endsect] [section `mp_rename`] template class Y> using mp_rename = /*...*/; `mp_rename` changes the type of the list `L` to `Y`. That is, `mp_rename, Y>` is an alias for `Y`. + + using L1 = std::pair; + using R1 = mp_rename; // std::tuple + + using L2 = std::tuple; + using R2 = mp_rename; // mp_list [endsect] [section `mp_apply`] @@ -90,12 +147,19 @@ is an alias for `L`. `mp_apply` applies the metafunction `F` to the contents of the list `L`, that is, `mp_apply>` is an alias for `F`. (`mp_apply` is the same as `mp_rename` with the arguments reversed.) + + using L1 = std::pair; + using R1 = mp_apply; // std::is_same [endsect] [section `mp_apply_q`] template using mp_apply_q = mp_apply; Same as `mp_apply`, but takes a quoted metafunction. + + using L1 = std::tuple; + using L2 = mp_list; + using R1 = mp_apply_q, L2>; // std::tuple [endsect] [section `mp_append`] @@ -103,6 +167,13 @@ Same as `mp_apply`, but takes a quoted metafunction. `mp_append` concatenates the lists in `L...` into a single list that has the same type as the first list. `mp_append<>` is an alias for `mp_list<>`. `mp_append, L2, ..., Ln>` is an alias for `L1`. + + using L1 = std::tuple; + using L2 = mp_list; + using L3 = std::pair; + using L4 = mp_list<>; + + using R1 = mp_append; // std::tuple [endsect] [section `mp_replace_front`] @@ -110,6 +181,15 @@ is an alias for `mp_list<>`. `mp_append, L2, ..., Ln>` i `mp_replace_front` replaces the first element of the list `L` with `T`. That is, `mp_replace_front, T>` is an alias for `L`. + + using L1 = std::pair; + using R1 = mp_replace_front; // std::pair + + using L2 = std::tuple; + using R2 = mp_replace_front; // std::tuple + + using L3 = mp_list; + using R3 = mp_replace_front; // mp_list; [endsect] [section `mp_replace_first`] @@ -123,6 +203,15 @@ an alias for `L`. `mp_replace_second` replaces the second element of the list `L` with `T`. That is, `mp_replace_second, T>` is an alias for `L`. + + using L1 = std::pair; + using R1 = mp_replace_second; // std::pair + + using L2 = std::tuple; + using R2 = mp_replace_second; // std::tuple + + using L3 = mp_list; + using R3 = mp_replace_second; // mp_list; [endsect] [section `mp_replace_third`] @@ -130,6 +219,12 @@ is an alias for `L`. `mp_replace_third` replaces the third element of the list `L` with `T`. That is, `mp_replace_third, T>` is an alias for `L`. + + using L1 = std::tuple; + using R1 = mp_replace_third; // std::tuple + + using L2 = mp_list; + using R2 = mp_replace_third; // mp_list; [endsect] [endsect:list]