Исключить первые N аргументов из пакета параметров


У меня есть функция foo, которая вызывает функцию bar с подмножеством типов перешел в foo'шаблонов с переменным количеством аргументов. Например:

template <typename... T>
void foo() {
  // ...
  template <size_t start_idx, typename... T>
  using param_pack = /*Parameter pack with T[start_idx]...T[N]*/
  auto b = bar<param_pack<2, T...>>();
  // ...
}

Есть ли способ извлечь "пакет субпараметров". В приведенном выше случае если T = [int float char double], то param_pack<2, T...> = [char double]

[EDIT]

Моя цель состоит в том, чтобы иметь возможность использовать что-то подобное для сопоставления обработчиков событий. Например

struct ev {};

template <typename... T>
struct event : ev {
  std::tuple<T...> data_;

  event(T&&... d) : data_(std::make_tuple(std::forward<T>(d)...)) {}
};

template <typename... Functor>
struct handler {
  std::tuple<Functor...> funcs_;

  handler(Functor&&... f) : funcs_(std::make_tuple(std::forward<Functor>(f)...)) {}

  void handle_message(ev* e) {
    auto ptrs = std::make_tuple(
      dynamic_cast<event<param_pack<1, typename function_traits<F>::args>>*>(e)...
    ); 

    match(ptrs);
  }
};

Здесь function_traits::args получить пакет параметров для аргументов функции и сопоставить итерации над кортежем funcs_ проверка успешности dynamic_cast и выполнение первой успешной функции. Я их уже реализовал.

Обработчики-это что-то вроде

[] (handler* self, <ARGS>) -> void {
  // ...
}
По существу, я пытаюсь избавиться от аргумента self.
3 2

3 ответа:

Отложим в сторону тот факт, что в нем отсутствует проверка индекса N для простоты, вот возможное решение, основанное на объявлении функции (определение не требуется) и объявлении using:

template<std::size_t N, typename... T, std::size_t... I>
std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>
sub(std::index_sequence<I...>);

template<std::size_t N, typename... T>
using subpack = decltype(sub<N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

Хорошая частьэтого подхода состоит в том, что вам не нужно вводить новый тип, разработанный вокруг кортежа, а затем каким-то образом итеративно специализировать его.


Он следует минимальному, рабочему примеру, который использует приведенный выше код:

#include<functional>
#include<tuple>
#include<cstddef>
#include<type_traits>

template<std::size_t N, typename... T, std::size_t... I>
std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>
sub(std::index_sequence<I...>);

template<std::size_t N, typename... T>
using subpack = decltype(sub<N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

int main() {
    static_assert(std::is_same<subpack<2, int, float, char, double>, std::tuple<char, double>>::value, "!");
}

Смотрите полный пример вверх и работает на wandbox .


Расширенная версия, включающая проверку индекса N, будет выглядеть следующим образом:

template<std::size_t N, typename... T, std::size_t... I>
std::enable_if_t<(N < sizeof...(T)), std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>>
sub(std::index_sequence<I...>);

Это тип, который вы можете увидеть в первом примере, когда-то завернутый в std::enable_if_t, не более. Опять же, Декларации достаточно, никакого определения не требуется.


EDIT

Если вы хотите использовать свой собственный шаблон класса вместо std::tuple, вы можете легко изменить код, чтобы сделать это:

#include<functional>
#include<tuple>
#include<cstddef>
#include<type_traits>

template<typename...>
struct bar {};

template<template<typename...> class C, std::size_t N, typename... T, std::size_t... I>
std::enable_if_t<(N < sizeof...(T)), C<std::tuple_element_t<N+I, std::tuple<T...>>...>>
sub(std::index_sequence<I...>);

template<template<typename...> class C, std::size_t N, typename... T>
using subpack = decltype(sub<C, N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

int main() {
    static_assert(std::is_same<subpack<bar, 2, int, float, char, double>, bar<char, double>>::value, "!");
}

EDIT

Согласно коду, добавленному к вопросу, приведенное выше решение остается в силе. Вы должны просто определить свой класс event следующим образом:

struct ev {};

template <typename>
struct event;

template <typename... T>
struct event<std::tuple<T...>>: ev {
    // ...
};

Вот так, когда вы делаете это:

event<param_pack<1, typename function_traits<F>::args>>

Вы все еще получаете кортеж из param_pack (это subpack, использующий объявление в моем примере), но он соответствует шаблонной частичной специализации event, и пакет параметров находится в вашем распоряжении как T....

Это и есть лучшее, что вы можете сделать, так как вы не можете поместить пакет параметров в объявление using. В любом случае это просто работает, так что, вероятно, это может решить вашу проблему.

Вы можете сделать что-то вроде:

template <std::size_t N, typename ... Ts> struct drop;

template <typename ... Ts>
struct drop<0, Ts...>
{
    using type = std::tuple<Ts...>;
};

template <std::size_t N, typename T, typename ... Ts>
struct drop<N, T, Ts...>
{
    using type = typename drop<N - 1, Ts...>;
};

// Specialization to avoid the ambiguity
template <typename T, typename... Ts>
struct drop<0, T, Ts...>
{
    using type = std::tuple<T, Ts...>;
};

Вот быстрое, но не особенно многоразовое решение.

template <typename Pack, std::size_t N, std::size_t... Is>
void invoke_bar_impl(std::index_sequence<Is...>) {
    bar<std::tuple_element_t<N + Is, Pack>...>();
}

template <std::size_t N, typename... Ts>
void invoke_bar() {
    auto indices = std::make_index_sequence<sizeof...(Ts) - N>();
    invoke_bar_impl<std::tuple<Ts...>, N>(indices);
}