【问题标题】:Get first N elements of parameter pack获取参数包的前 N ​​个元素
【发布时间】:2017-02-27 23:41:42
【问题描述】:

我必须解决以下问题:

template< size_t... N_i >
class A
{
  // ...
};

template< size_t N, size_t... N_i >
A</* first N elements of N_i...*/> foo()
{
  A</* first N elements of N_i...*/> a;

  // ...

  return a;
}

int main()
{
  A<1,2> res = foo<2, 1,2,3,4>();

  return 0;
}

在这里,我希望foo 具有返回类型A&lt;/* first N size_t of N_i...*/&gt;,即class A,它具有作为模板参数的参数包的前N 个元素N_i

有人知道如何实现吗?

【问题讨论】:

  • 大量挖掘递归
  • 谢谢。没有“优雅”的解决方案吗?
  • @abraham_hilbert:您正在做 C++ 模板元编程,并且想要一个“优雅”的解决方案? ??????
  • @LightnessRacesinOrbit 有时并非不可能 ;)
  • @krzaq 也可能这次这不是不可能的。 ;-)

标签: c++ c++14 variadic-templates


【解决方案1】:

这是我想到的最短的解决方案(用了两行作为别名)。
它遵循基于 OP 发布的代码的最小工作示例:

#include<functional>
#include<cstddef>
#include<utility>
#include<tuple>

template<std::size_t... V>
class A {};

template<std::size_t... V, std::size_t... I>
constexpr auto func(std::index_sequence<I...>) {
    return A<std::get<I>(std::make_tuple(V...))...>{};
}

template<std::size_t N, std::size_t... V>
constexpr auto func() {
    return func<V...>(std::make_index_sequence<N>{});
}

template<std::size_t N, std::size_t... V>
using my_a = decltype(func<N, V...>());

int main() {
    A<1,2> res1 = func<2, 1, 2, 3, 4>();
    // Or even better...
    decltype(func<2, 1, 2, 3, 4>()) res2{};
    // Or even better...
    my_a<2, 1, 2, 3, 4> res3{};
}

【讨论】:

  • 这是constexprness of make_tupleget 的一个很好的用法。
  • @krzaq 谢谢。希望这对 OP 有所帮助。
【解决方案2】:

这是@skypjack's answer 的一个细微变化,避免使用元组:

template <size_t... N_i,size_t... M_i>
auto foo2(std::index_sequence<M_i...>)
{
    constexpr size_t values[] = {N_i...};
    return A<values[M_i]...>();
}

template <size_t N,size_t... N_i>
auto foo()
{
    return foo2<N_i...>(std::make_index_sequence<N>());
}

【讨论】:

  • 您在轻微变化中遗漏了一些东西。因为它不能再在常量表达式中使用。您应该在函数前面添加constexpr。如果您决定复制答案,请复制到最后。 ;-)
【解决方案3】:

最直接的子问题在于类型列表:

template <class... Ts>
struct typelist {
    using type = typelist;
    static constexpr std::size_t size = sizeof...(Ts);
};

template <class T>
struct tag { using type = T; };

template <std::size_t N, class TL>
struct head_n {
    using type = ???;
};

现在,head_n 只是一个简单的递归问题 - 从一个空列表开始将元素从一个列表移动到另一个列表 N 次。

template <std::size_t N, class R, class TL>
struct head_n_impl;

// have at least one to pop from and need at least one more, so just 
// move it over
template <std::size_t N, class... Ts, class U, class... Us>
struct head_n_impl<N, typelist<Ts...>, typelist<U, Us...>>
: head_n_impl<N-1, typelist<Ts..., U>, typelist<Us...>>
{ };

// we have two base cases for 0 because we need to be more specialized
// than the previous case regardless of if we have any elements in the list
// left or not
template <class... Ts, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<Us...>>
: tag<typelist<Ts...>>
{ };

template <class... Ts, class U, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<U, Us...>>
: tag<typelist<Ts...>>
{ };

template <std::size_t N, class TL>
using head_n = typename head_n_impl<N, typelist<>, TL>::type;

从这个到你的具体问题我留给读者作为练习。


另一种方法是通过串联。将typelist&lt;Ts...&gt; 的每个元素转换为typelist&lt;T&gt;typelist&lt;&gt;,然后将它们连接在一起。 concat 直截了当:

template <class... Ts>
struct concat { };

template <class TL>
struct concat<TL>
: tag<TL>
{ };

template <class... As, class... Bs, class... Rest>
struct concat<typelist<As...>, typelist<Bs...>, Rest...>
: concat<typelist<As..., Bs...>, Rest...>
{ };

然后我们可以这样做:

template <std::size_t N, class TL, class = std::make_index_sequence<TL::size>>
struct head_n;

template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
: concat<
        std::conditional_t<(Is < N), typelist<Ts>, typelist<>>...
        >
{ };

template <std::size_t N, class TL>
using head_n_t = typename head_n<N, TL>::type;

后一种方法的优点是,在 C++17 中,concat 可以在给定适当的operator+ 的情况下被折叠表达式替换:

template <class... As, class... Bs>
constexpr typelist<As..., Bs...> operator+(typelist<As...>, typelist<Bs...> ) {
    return {};
}

允许:

template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
{
    using type = decltype(
        (std::conditional_t<(Is < N), typelist<Ts>, typelist<>>{} + ... + typelist<>{})
        );
};        

【讨论】:

    【解决方案4】:

    Boost.Hana 相当简单:

    namespace hana = boost::hana;
    
    template<size_t... vals>
    auto make_a(hana::tuple<hana::integral_constant<size_t, vals>...>)
    {
        return A<vals...>{};
    }
    
    template<size_t N, size_t... vals>
    auto foo(){
        constexpr auto front = hana::take_front(
            hana::tuple_c<size_t, vals...>,
            hana::integral_c<size_t,N>
        );
        return detail::make_a(front);
    }
    

    live demo

    【讨论】:

      【解决方案5】:

      您还可以使用可变参数通用 lambda 表达式和可重用的辅助结构来执行编译时迭代:

      #include <utility>
      #include <tuple>
      
      template <std::size_t N, class = std::make_index_sequence<N>>
      struct iterate;
      
      template <std::size_t N, std::size_t... Is>
      struct iterate<N, std::index_sequence<Is...>> {
         template <class Lambda>
         auto operator()(Lambda lambda) {
            return lambda(std::integral_constant<std::size_t, Is>{}...);
         }
      };
      
      template <size_t... Is>
      struct A { };
      
      template <size_t N, size_t... Is>
      auto foo() {
         return iterate<N>{}([](auto... ps){
            using type = std::tuple<std::integral_constant<std::size_t, Is>...>;
            return A<std::tuple_element_t<ps, type>{}...>{};
         });
      }
      
      int main() {
         decltype(foo<3, 1, 2, 3, 4>()) a; // == A<1, 2, 3> a;
      }
      

      【讨论】:

      • @skypjack 顺便说一句,我实际上认为这个解决方案一点也不棘手,并且可能相当可扩展:)
      • 不要误会,我喜欢你结合默认参数、偏特化和 lambda 函数的方式。我只是想这对于特定情况来说太棘手了。我属于如果你能做到这一点的一方,你不应该浪费下面的一方。就这样。无论如何,很好的解决方案。 ;-)
      • 我是两三个厘米前买的。 :-D ...我明白你的意思,但我仍然想你可以把它写成更紧凑的形式。无论如何,我喜欢这个,因为它是一个大脑健身房。 ;-)
      • @skypjack hah brain-gym 我要记住 :) 好吧,你把我和 I'm of the party for which if you can do it in a line, you should not waste the one below 评论弄糊涂了 :) 老实说,我也认为你的解决方案实际上可能是 OP 的最佳解决方案,但也许将来其他人可能会在他或她的特定问题中找到更好的代码:)
      • 您可以将decltype(ps)::value 缩减为ps。和整个tuple_element 东西到A&lt;std::tuple_element_t&lt;ps, type&gt;{}...&gt;{}
      【解决方案6】:

      不幸的是,这种方法需要定义额外的 Helper 类型

      template< size_t... N_i >
      class A
      {
      };
      
      template <size_t... N_i>
      struct Helper;
      
      template <size_t... N_i>
      struct Helper<0, N_i...>
      {
          typedef A<> type;
      };
      
      template <size_t N0, size_t... N_i>
      struct Helper<1, N0, N_i...>
      {
          typedef A<N0> type;
      };
      
      template <size_t N0, size_t N1, size_t... N_i>
      struct Helper<2, N0, N1, N_i...>
      {
          typedef A<N0, N1> type;
      };
      
      template< size_t N, size_t... N_i >
      typename Helper<N, N_i...>::type foo()
      {
        typename Helper<N, N_i...>::type a;
        return a;
      }
      

      【讨论】:

        猜你喜欢
        • 2011-04-12
        • 1970-01-01
        • 1970-01-01
        • 2014-09-17
        • 1970-01-01
        • 2022-07-01
        • 2016-04-25
        • 2020-07-22
        相关资源
        最近更新 更多