添加两个可变参数的整数列表

时间:2019-05-21 09:48:35

标签: c++ variadic-templates

给出这种类型

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;
    }
};

我想实现一个函数add,该函数返回一个新列表,其中包含两个输入列表的逐元素相加。 换句话说(伪代码):

add([a0, a1, a2], [b0, b1]) -> [a0 + b0, a1 + b2, a2]

问题:

  • 我什至不知道如何声明此类函数的返回类型
  • 我不知道是否可能

4 个答案:

答案 0 :(得分:4)

这是我要怎么做:

#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);
}

此外,请注意,size_t N不需要class list模板参数。参数包知道它们自己的大小。

答案 1 :(得分:3)

另一种基于旧的部分专业化的解决方案:

 routes.MapRoute(
                     name: "EventDetails",
                      url: "Събитие-Детайли",
                       defaults: new { controller = "Admin", action =     "EventDetails" }
                 );

<a class="btn btn-link" href="@Url.Action("EventDetails", new { IdNews=@evnt.IdNews})"  style="color:darkgray;font-size:12px;text-decoration:underline"><i>прочети повече>>></i></a>

public ActionResult EventDetails(int IdNews, bool IsPublic = false)

可以用作:

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>; }

实时演示:https://wandbox.org/permlink/x8LYcoC3lWu51Gqo

答案 2 :(得分:3)

另一种基于std::integer_sequence的解决方案。

当两个列表的维数相同时,add()函数非常简单

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, 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>{}); }

我们只需要添加以下add()函数

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); }

下面是完整编译的C ++ 14(不幸的是,std::make_index_sequence / std::index_sequence需要C ++ 14)示例

#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,
                  "!" );
 }

答案 3 :(得分:2)

我将使用std::integer_sequence

template <int ... Is>
using int_sequence = std::integer_sequence<int, Is...>;

匹配大小时,这很容易,因此请创建增加大小并填充零的方法:

template <int... Is, int... Zeros>
int_sequence<Is..., (0 * Zeros)...>
fill_with_zero(int_sequence<Is...>, int_sequence<Zeros...>) { return {}; }

template <std::size_t N, int... Is>
auto fill_with_zero_to_reach(int_sequence<Is...> seq)
-> decltype(fill_with_zero(seq,
                           std::make_integer_sequence<int,
                                                      (sizeof...(Is) < N
                                                       ? N - sizeof...(Is)
                                                       : 0)>{}))
{
    return {};
}

然后添加:

// simple case, sizes match:
template <int... Is1, int... Is2,
          std::enable_if_t<sizeof...(Is1) == sizeof...(Is2), int> = 0>
int_sequence<(Is1 + Is2)...>
add(int_sequence<Is1...>, int_sequence<Is2...>) { return {}; }

// sizes mismatch:
template <int... Is1, int... Is2,
          std::enable_if_t<sizeof...(Is1) != sizeof...(Is2), int> = 0>
auto
add(int_sequence<Is1...> seq1, int_sequence<Is2...> seq2)
-> decltype(add(fill_with_zero_to_reach<std::max(sizeof...(Is1), sizeof...(Is2))>(seq1),
                fill_with_zero_to_reach<std::max(sizeof...(Is1), sizeof...(Is2))>(seq2)))
{ return {}; }

Demo