Исключить первые 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 ответа:
Отложим в сторону тот факт, что в нем отсутствует проверка индекса
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); }