J'ai une fonction de modèle variadique foo():

template <typename... Args>
void foo(Args &&... args);

Cette fonction est destinée à être appelée avec tous les arguments de size_t. Je peux appliquer cela en utilisant une certaine métaprogrammation. Je dois prendre la liste d'arguments résultante deux à la fois et les mettre dans un conteneur de std::pair<size_t, size_t>. Conceptuellement, quelque chose comme:

std::vector<std::pair<size_t, size_t> > = { 
    std::make_pair(args[0], args[1]), 
    std::make_pair(args[2], args[3]), ...
};

Existe-t-il un moyen simple de le faire? Je sais que par extension de pack, je pourrais mettre les arguments dans un conteneur plat, mais y a-t-il un moyen de les regrouper deux par deux dans des objets std::pair en même temps?

4
Jason R 24 janv. 2017 à 22:51

4 réponses

Meilleure réponse

L'indexation dans des packs n'est pas vraiment faisable (encore?), Mais l'indexation dans des tuples l'est. Il suffit de tout coller dans un tuple, puis de tout retirer au fur et à mesure. Puisque tout est un size_t, nous pouvons simplement copier:

template <size_t... Is, class Tuple>
std::vector<std::pair<size_t, size_t>> 
foo_impl(std::index_sequence<Is...>, Tuple tuple) {
    return std::vector<std::pair<size_t, size_t> >{ 
        std::make_pair(std::get<2*Is>(tuple), std::get<2*Is+1>(tuple))...
    };
}

template <typename... Args>
void foo(Args... args)
{
    auto vs = foo_impl(std::make_index_sequence<sizeof...(Args)/2>{},
        std::make_tuple(args...));

    // ...
}
8
Barry 24 janv. 2017 à 20:06

Avec range-v3, vous pouvez faire

template <typename... Args>
void foo(Args&&... args)
{
    std::initializer_list<std::size_t> nbs = {static_cast<std::size_t>(args)...};
    const auto pair_view =
        ranges::view::zip(nbs | ranges::view::stride(2),
                          nbs | ranges::view::drop(1) |  ranges::view::stride(2));

    // And possibly
    std::vector<std::pair<std::size_t, std::size_t>> pairs = pair_view;
    // ...
}

Démo

2
Jarod42 24 janv. 2017 à 20:29

Quelqu'un ( toux @Barry toux ) a dit que l'indexation dans les packs n'est pas possible.

C'est C ++. Impossible signifie que nous ne l'avons tout simplement pas encore écrit.

template<std::size_t I> struct index_t:std::integral_constant<std::size_t, I> {
  using std::integral_constant<std::size_t, I>::integral_constant;
  template<std::size_t J>
  constexpr index_t<I+J> operator+( index_t<J> ) const { return {}; }
  template<std::size_t J>
  constexpr index_t<I-J> operator-( index_t<J> ) const { return {}; }
  template<std::size_t J>
  constexpr index_t<I*J> operator*( index_t<J> ) const { return {}; }
  template<std::size_t J>
  constexpr index_t<I/J> operator/( index_t<J> ) const { return {}; }
};
template<std::size_t I>
constexpr index_t<I> index{};

template<std::size_t B>
constexpr index_t<1> exponent( index_t<B>, index_t<0> ) { return {}; }

template<std::size_t B, std::size_t E>
constexpr auto exponent( index_t<B>, index_t<E> ) {
  return index<B> * exponent( index<B>, index<E-1> );
}
template<std::size_t N>
constexpr index_t<0> from_base(index_t<N>) { return {}; }
template<std::size_t N, std::size_t c>
constexpr index_t<c-'0'> from_base(index_t<N>, index_t<c>) { return {}; }
template<std::size_t N, std::size_t c0, std::size_t...cs>
constexpr auto from_base(index_t<N>, index_t<c0>, index_t<cs>...) {
  return 
    from_base(index<N>, index<c0>) * exponent(index<N>, index<sizeof...(cs)>)
    + from_base(index<N>, index<cs>...)
  ;
}

template<char...cs>
constexpr auto operator""_idx(){
  return from_base(index<10>, index<cs>...);
}

auto nth = [](auto index_in){
  return [](auto&&...elems)->decltype(auto){
    using std::get;
    constexpr auto I= index<decltype(index_in){}>;
    return get<I>(std::forward_as_tuple(decltype(elems)(elems)...));
  };
};

Maintenant, nous obtenons:

using pair_vec = std::vector<std::pair<std::size_t, std::size_t>>;
template <typename... Args>
pair_vec foo(Args &&... args) {
  return
    index_over< sizeof...(args)/2 >()
    ([&](auto...Is)->pair_vec{
      return {
        {
          nth( Is*2_idx )( decltype(args)(args)... ),
          nth( Is*2_idx+1_idx )( decltype(args)(args)... )
        }...
      };
    });
}

Où nous indexons "directement" nos packs de paramètres en utilisant des index constants de temps de compilation.

exemple en direct.

1
Yakk - Adam Nevraumont 24 janv. 2017 à 21:38

Supposons que vous soyez autorisé à refactoriser votre logique en une fonction d'assistance interne:

template <typename ...Args>
void foo(Args &&... args)
{
    foo_impl(std::make_index_sequence<sizeof...(Args) / 2>(),
             std::forward<Args>(args)...);
}

Nous pouvons maintenant opérer sur l'index du pack d'arguments par index:

template <std::size_t ...I, typename ...Args>
void foo_impl(std::index_sequence<I...>, Args &&... args)
{
    std::vector<std::pair<std::size_t, std::size_t>> v =
        { GetPair(std::integral_constant<std::size_t, I>(), args...)... };
}

Il reste à implémenter l'extracteur de paires:

template <typename A, typename B, typename ...Tail>
std::pair<std::size_t, std::size_t> GetPair(std::integral_constant<std::size_t, 0>,
                                            A a, B b, Tail ... tail)
{
    return { a, b };
}

template <std::size_t I, typename A, typename B, typename ...Tail>
std::pair<std::size_t, std::size_t> GetPair(std::integral_constant<std::size_t, I>,
                                            A a, B b, Tail ... tail)
{
    return GetPair<I - 1>(tail...);
}
2
Kerrek SB 24 janv. 2017 à 20:08