获取参数包的前N个元素

abr*_*ert 21 c++ variadic-templates c++14

我必须遵循以下问题:

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;
}
Run Code Online (Sandbox Code Playgroud)

在这里,我想要foo返回类型A</* first N size_t of N_i...*/>,即class A具有参数包的前N个元素作为模板参数N_i.

有谁知道这是如何实现的?

sky*_*ack 15

这是我脑海中最短的解决方案(两行用于别名).
它遵循一个基于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{};
}
Run Code Online (Sandbox Code Playgroud)

  • 这是`make_tuple`和`get`的`constexpr`ness的很好用. (3认同)

Bar*_*rry 8

最直接的子问题是在类型列表的土地上:

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 = ???;
};
Run Code Online (Sandbox Code Playgroud)

现在,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;
Run Code Online (Sandbox Code Playgroud)

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


另一种方法是通过连接.将a的每个元素转换typelist<Ts...>为a typelist<T>或a typelist<>,然后将它们连接在一起.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...>
{ };
Run Code Online (Sandbox Code Playgroud)

然后我们可以做到:

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;
Run Code Online (Sandbox Code Playgroud)

后一种方法的优点是concat可以在C++ 17中通过给定适当的fold-expression替换operator+:

template <class... As, class... Bs>
constexpr typelist<As..., Bs...> operator+(typelist<As...>, typelist<Bs...> ) {
    return {};
}
Run Code Online (Sandbox Code Playgroud)

这使得:

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<>{})
        );
};        
Run Code Online (Sandbox Code Playgroud)


Vau*_*ato 7

@ skypjack的答案略有不同,避免使用元组:

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>());
}
Run Code Online (Sandbox Code Playgroud)

  • 你在_轻微变化_中遗漏了一些东西。照原样,它不能再用于常量表达式中。您应该在函数前添加一个 `constexpr`。如果您决定复制答案,请复制到底。;-) (2认同)