Given this type
template<std::size_t N, int ...content>
struct list {
inline int reduce() {
int result = 0;
constexpr int arr[N] = { content... };
for(std::size_t k = 0; k < N; ++k) {
result += arr[k];
}
return result;
}
};
I'd like to implement a function add
, which returns a new list containing the element-by-element addition of the two input lists.
In other words (pseudo-code):
add([a0, a1, a2], [b0, b1]) -> [a0 + b0, a1 + b2, a2]
Problem:
Here is how I would do it:
#include <iostream>
#include <utility>
template<std::size_t N, int ...content>
struct list {
inline int reduce() {
int result = 0;
constexpr int arr[N] = { content... };
for(std::size_t k = 0; k < N; ++k) {
result += arr[k];
}
return result;
}
};
template <std::size_t I, int ...A>
constexpr int list_at(list<sizeof...(A),A...>)
{
if constexpr (I < sizeof...(A))
{
constexpr int arr[] {A...};
return arr[I];
}
else
{
return 0;
}
}
template <int ...A, int ...B, std::size_t ...I>
constexpr auto list_sum_low(list<sizeof...(A),A...>,
list<sizeof...(B),B...>,
std::index_sequence<I...>)
{
return list<sizeof...(I), (list_at<I>(list<sizeof...(A),A...>{}) +
list_at<I>(list<sizeof...(B),B...>{}))...>{};
}
template <int ...A, int ...B>
constexpr auto list_sum(list<sizeof...(A),A...>, list<sizeof...(B),B...>)
{
constexpr int a = sizeof...(A), b = sizeof...(B);
return list_sum_low(list<a,A...>{}, list<b,B...>{},
std::make_index_sequence<(a > b ? a : b)>{});
}
template <int ...A>
void print_list(list<sizeof...(A),A...>)
{
(void(std::cout << ' ' << A) , ...);
}
int main()
{
constexpr auto x = list_sum(list<4, 1,2,3,4>{}, list<2, 10,20>{});
print_list(x);
}
Also, note that there is no need have size_t N
template parameter for class list
. Parameter packs know their own size.
Just another solution, based on good old partial specialization:
template <size_t N, int... E> struct list { };
template <typename, typename> struct list_cat;
template <size_t N1, int... E1, size_t N2, int... E2>
struct list_cat<list<N1, E1...>, list<N2, E2...>>
{ using type = list<N1 + N2, E1..., E2...>; };
template <typename, typename> struct list_add;
template <size_t N1, int E1H, int... E1T, size_t N2, int E2H, int... E2T>
struct list_add<list<N1, E1H, E1T...>, list<N2, E2H, E2T...>>
{
using type = typename list_cat<
list<1, E1H + E2H>,
typename list_add<list<N1 - 1, E1T...>, list<N2 - 1, E2T...>>::type
>::type;
};
template <size_t N2, int... E2>
struct list_add<list<0>, list<N2, E2...>> { using type = list<N2, E2...>; };
template <size_t N1, int... E1>
struct list_add<list<N1, E1...>, list<0>> { using type = list<N1, E1...>; };
template <>
struct list_add<list<0>, list<0>> { using type = list<0>; }
Which can be used as:
using L1 = list<3, -1, -2, -3>;
using L2 = list <2, 10, 20>;
using L = typename list_add<L1, L2>::type;
Live demo: https://wandbox.org/permlink/x8LYcoC3lWu51Gqo
Another solution std::integer_sequence
based.
When the dimension of the two lists is the same, an add()
function is trivially simple
template <std::size_t N, int ... Is1, int ... Is2>
constexpr auto add (list<N, Is1...>, list<N, Is2...>)
{ return list<N, Is1+Is2...>{}; }
The problem is when we have lists of different length.
A possible solution is extend the shorter list with zeros and apply the preceding function to the length-uniformed lists.
Given a extender as follows
template <std::size_t N1, std::size_t N0, int ... Is, std::size_t ... Js>
constexpr auto listExtend (list<N0, Is...>, std::index_sequence<Js...>)
{ return list<N1, Is..., ((void)Js, 0)...>{}; }
template <std::size_t N1, std::size_t N0, int ... Is,
std::enable_if_t<(N1 > N0), bool> = true>
constexpr auto listExtend (list<N0, Is...> l)
{ return listExtend<N1>(l, std::make_index_sequence<N1-N0>{}); }
we need only to add the following add()
functions
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2,
std::enable_if_t<(N1 > N2), bool> = true>
constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2)
{ return add(l1, listExtend<N1>(l2)); }
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2,
std::enable_if_t<(N1 < N2), bool> = true>
constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2)
{ return add(listExtend<N2>(l1), l2); }
The following is a full compiling C++14 (unfortunately std::make_index_sequence
/std::index_sequence
require C++14) example
#include <utility>
#include <type_traits>
template <std::size_t, int ...>
struct list
{ };
template <std::size_t N1, std::size_t N0, int ... Is, std::size_t ... Js>
constexpr auto listExtend (list<N0, Is...>, std::index_sequence<Js...>)
{ return list<N1, Is..., ((void)Js, 0)...>{}; }
template <std::size_t N1, std::size_t N0, int ... Is,
std::enable_if_t<(N1 > N0), bool> = true>
constexpr auto listExtend (list<N0, Is...> l)
{ return listExtend<N1>(l, std::make_index_sequence<N1-N0>{}); }
template <std::size_t N, int ... Is1, int ... Is2>
constexpr auto add (list<N, Is1...>, list<N, Is2...>)
{ return list<N, Is1+Is2...>{}; }
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2,
std::enable_if_t<(N1 > N2), bool> = true>
constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2)
{ return add(l1, listExtend<N1>(l2)); }
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2,
std::enable_if_t<(N1 < N2), bool> = true>
constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2)
{ return add(listExtend<N2>(l1), l2); }
int main ()
{
list<3u, 1, 2, 3> l1;
list<2u, 10, 20> l2;
auto l3 = add(l1, l2);
static_assert( std::is_same<decltype(l3), list<3u, 11, 22, 3>>::value,
"!" );
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With