2013-02-13 4 views
22

Это, кажется, очень простой вопрос: как удалить первый (n-й) тип в std::tuple?Удаление первого типа std :: tuple

Пример:

typedef std::tuple<int, short, double> tuple1; 
typedef std::tuple<short, double> tuple2; 

описанной выше операции превратит tuple1 в tuple2. Является ли это возможным?

+0

перегрузки '' оператор =, который принимает эти два типа 'tuple' s? –

+2

Краткое описание реализации, так что кто-то со способностью * и временем *, чтобы сделать ответ, может сделать это :): создать пакет индексов без 0, а переменный расширять 'get ' на 'make_tuple'. –

+0

@meh: Это не работает, потому что меня интересуют типы, а не объекты. – cschwan

ответ

26

Вы можете использовать простую функцию типа на основе частичной специализации шаблона класса:

#include <type_traits> 
#include <tuple> 

using namespace std; 

template<typename T> 
struct remove_first_type 
{ 
}; 

template<typename T, typename... Ts> 
struct remove_first_type<tuple<T, Ts...>> 
{ 
    typedef tuple<Ts...> type; 
}; 

int main() 
{ 
    typedef tuple<int, bool, double> my_tuple; 
    typedef remove_first_type<my_tuple>::type my_tuple_wo_first_type; 

    static_assert(
     is_same<my_tuple_wo_first_type, tuple<bool, double>>::value, 
     "Error!" 
     ); 
} 

Кроме того, это решение может быть легко обобщается для удаления я-й типа кортежа:

#include <type_traits> 
#include <tuple> 

using namespace std; 

template<size_t I, typename T> 
struct remove_ith_type 
{ 
}; 

template<typename T, typename... Ts> 
struct remove_ith_type<0, tuple<T, Ts...>> 
{ 
    typedef tuple<Ts...> type; 
}; 

template<size_t I, typename T, typename... Ts> 
struct remove_ith_type<I, tuple<T, Ts...>> 
{ 
    typedef decltype(
     tuple_cat(
      declval<tuple<T>>(), 
      declval<typename remove_ith_type<I - 1, tuple<Ts...>>::type>() 
      ) 
     ) type; 
}; 

int main() 
{ 
    typedef tuple<int, bool, double> my_tuple; 
    typedef remove_ith_type<1, my_tuple>::type my_tuple_wo_2nd_type; 

    static_assert(
     is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value, 
     "Error!" 
     ); 
} 
+0

Aah, частичная специализация! Спасибо, вот и все! – cschwan

+0

+1, шаблон мета-программирования так классный :) – StoryTeller

+1

@cschwan: Добро пожаловать :-) Я добавил обобщенную версию, чтобы удалить i-й тип из кортежа. –

3

Я придумал решение, очень похожее на предложение, предложенное @Andy, но оно пытается быть более общим, работая непосредственно с пакетом параметров (используя фиктивную обертку), а не на std::tuple. Таким образом, операция может быть применена к другим переменным числом шаблонов, а не только кортежей:

#include <type_traits> 
#include <tuple> 

template <typename... Args> struct pack {}; 

template <template <typename...> class T, typename Pack> 
struct unpack; 

template <template <typename...> class T, typename... Args> 
struct unpack<T, pack<Args...>> 
{ 
    typedef T<Args...> type; 
}; 

template <typename T, typename Pack> 
struct prepend; 

template <typename T, typename... Args> 
struct prepend<T, pack<Args...>> 
{ 
    typedef pack<T, Args...> type; 
}; 

template <std::size_t N, typename... Args> 
struct remove_nth_type; 

template <std::size_t N, typename T, typename... Ts> 
struct remove_nth_type<N, T, Ts...> 
    : prepend<T, typename remove_nth_type<N-1, Ts...>::type> 
{}; 

template <typename T, typename... Ts> 
struct remove_nth_type<0, T, Ts...> 
{ 
    typedef pack<Ts...> type; 
}; 

template <typename T, int N> 
struct remove_nth; 

template <template <typename...> class T, int N, typename... Args> 
struct remove_nth<T<Args...>, N> 
{ 
    typedef typename 
     unpack< 
      T, typename 
      remove_nth_type<N, Args...>::type 
     >::type type; 
}; 

template <typename... Args> 
struct my_variadic_template 
{ 
}; 

int main() 
{ 
    typedef std::tuple<int, bool, double> my_tuple; 
    typedef remove_nth<my_tuple, 1>::type my_tuple_wo_2nd_type; 

    static_assert(
     is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value, 
     "Error!" 
     ); 

    typedef my_variadic_template<int, double> vt; 
    typedef remove_nth<vt, 0>::type vt_wo_1st_type; 

    static_assert(
     is_same<vt_wo_1st_type, my_variadic_template<double>>::value, 
     "Error!" 
     ); 
} 

pack является помощником структура, единственной целью которого является сохранение параметра шаблона пакета. unpack можно использовать для распаковки параметров в произвольный шаблон класса (thanks to @BenVoigt for this trick). prepend просто добавляет тип к упаковке.

remove_nth_type использует частичную специализированную специализацию для удаления n-го типа из пакета параметров, сохраняя результат в pack. Наконец, remove_nth берет специализацию произвольного шаблона класса, удаляет n-й тип из его параметров шаблона и возвращает новую специализацию.

8

я написал proposal, который был принят в стандартный, что делает его очень легко сделать для любого типа «кортеж типа», то есть один C++ 14, который поддерживает tuple_size и tuple_element API:

template<typename T, typename Seq> 
    struct tuple_cdr_impl; 

template<typename T, std::size_t I0, std::size_t... I> 
    struct tuple_cdr_impl<T, std::index_sequence<I0, I...>> 
    { 
     using type = std::tuple<typename std::tuple_element<I, T>::type...>; 
    }; 

template<typename T> 
    struct tuple_cdr 
    : tuple_cdr_impl<T, std::make_index_sequence<std::tuple_size<T>::value>> 
    { }; 

И вы можете преобразовать объект кортежа в новый тип только с парой функций:

template<typename T, std::size_t I0, std::size_t... I> 
typename tuple_cdr<typename std::remove_reference<T>::type>::type 
cdr_impl(T&& t, std::index_sequence<I0, I...>) 
{ 
    return std::make_tuple(std::get<I>(t)...); 
} 

template<typename T> 
typename tuple_cdr<typename std::remove_reference<T>::type>::type 
cdr(T&& t) 
{ 
    return cdr_impl(std::forward<T>(t), 
        std::make_index_sequence<std::tuple_size<T>::value>{}); 
} 

Это создает целую последовательность [0,1,2,...,N) где N является tuple_size<T>::value, затем создает новый кортеж с make_tuple(get<I>(t)...) для I в [1,2,...,N)

тестирования:

using tuple1 = std::tuple<int, short, double>; 
using tuple2 = std::tuple<short, double>; 
using transformed = decltype(cdr(std::declval<tuple1>())); 
static_assert(std::is_same<transformed, tuple2>::value, ""); 
static_assert(std::is_same<tuple_cdr<tuple1>::type, tuple2>::value, ""); 


#include <iostream> 

int main() 
{ 
    auto t = cdr(std::make_tuple(nullptr, "hello", "world")); 
    std::cout << std::get<0>(t) << ", " << std::get<1>(t) << '\n'; 
} 

Моей эталонная реализация этого предложения находится на https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h

+0

Мне нравится это предложение и уже несколько раз оказываюсь в необходимости этой функции. До сих пор я вручную кодировал его, но стандартная реализация была бы неплохой. Надеюсь, он получит одобрение. –

+0

Спасибо, я буду отстаивать это на следующем заседании комитета, поэтому любые комментарии к деталям будут оценены (я не уверен, что общий шаблон переполнен, и требуется только 'int_seq', и я думаю, что собираюсь предложите 'apply()' следует добавить в). Мой адрес электронной почты легко найти в списках рассылки GCC. –

+1

Хорошее предложение. Вероятно, я бы добавил, что вы можете создать такой список в сложности «O (log N)». См. [Здесь] (http://stackoverflow.com/a/13073076/500104). Кроме того, разработчики компилятора могут захотеть использовать свои встроенные функции (у IIRC, GCC и Clang есть что-то похожее на '__builtin_make_indices'). – Xeo

1

Это более инженерный бито template метапрограммирования для решения этой задачи ,Она включает в себя способность выполнять произвольные упорядочивает/дупликации/абсорбцию о типах tuple через фильтр template:

#include <utility> 
#include <type_traits> 

template<typename... Ts> struct pack {}; 

template<std::size_t index, typename Pack, typename=void> struct nth_type; 

template<typename T0, typename... Ts> 
struct nth_type<0, pack<T0, Ts...>, void> { typedef T0 type; }; 

template<std::size_t index, typename T0, typename... Ts> 
struct nth_type<index, pack<T0, Ts...>, typename std::enable_if<(index>0)>::type>: 
    nth_type<index-1, pack<Ts...>> 
{}; 

template<std::size_t... s> struct seq {}; 

template<std::size_t n, std::size_t... s> 
struct make_seq:make_seq<n-1, n-1, s...> {}; 

template<std::size_t... s> 
struct make_seq<0,s...> { 
    typedef seq<s...> type; 
}; 

template<typename T, typename Pack> struct conc_pack { typedef pack<T> type; }; 
template<typename T, typename... Ts> struct conc_pack<T, pack<Ts...>> { typedef pack<T, Ts...> type; }; 

template<std::size_t n, typename Seq> struct append; 
template<std::size_t n, std::size_t... s> 
struct append<n, seq<s...>> { 
    typedef seq<n, s...> type; 
}; 
template<typename S0, typename S1> struct conc; 
template<std::size_t... s0, std::size_t... s1> 
struct conc<seq<s0...>, seq<s1...>> 
{ 
    typedef seq<s0..., s1...> type; 
}; 

template<typename T, typename=void> struct value_exists:std::false_type {}; 

template<typename T> struct value_exists<T, 
    typename std::enable_if< std::is_same<decltype(T::value),decltype(T::value)>::value >::type 
>:std::true_type {}; 

template<typename T, typename=void> struct result_exists:std::false_type {}; 
template<typename T> struct result_exists<T, 
    typename std::enable_if< std::is_same<typename T::result,typename T::result>::value >::type 
>:std::true_type {}; 

template<template<std::size_t>class filter, typename Seq, typename=void> 
struct filter_seq { typedef seq<> type; }; 

template<template<std::size_t>class filter, std::size_t s0, std::size_t... s> 
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<value_exists<filter<s0>>::value>::type> 
: append< filter<s0>::value, typename filter_seq<filter, seq<s...>>::type > 
{}; 

template<template<std::size_t>class filter, std::size_t s0, std::size_t... s> 
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && result_exists<filter<s0>>::value>::type> 
: conc< typename filter<s0>::result, typename filter_seq<filter, seq<s...>>::type > 
{}; 

template<template<std::size_t>class filter, std::size_t s0, std::size_t... s> 
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && !result_exists<filter<s0>>::value>::type> 
: filter_seq<filter, seq<s...>> 
{}; 

template<typename Seq, typename Pack> 
struct remap_pack { 
    typedef pack<> type; 
}; 

template<std::size_t s0, std::size_t... s, typename Pack> 
struct remap_pack< seq<s0, s...>, Pack > 
{ 
    typedef typename conc_pack< typename nth_type<s0, Pack>::type, typename remap_pack< seq<s...>, Pack >::type >::type type; 
}; 

template<typename Pack> 
struct get_indexes { typedef seq<> type; }; 

template<typename... Ts> 
struct get_indexes<pack<Ts...>> { 
    typedef typename make_seq< sizeof...(Ts) >::type type; 
}; 

template<std::size_t n> 
struct filter_zero_out { enum{ value = n }; }; 

template<> 
struct filter_zero_out<0> {}; 

template<std::size_t n> 
struct filter_zero_out_b { typedef seq<n> result; }; 

template<> 
struct filter_zero_out_b<0> { typedef seq<> result; }; 

#include <iostream> 

int main() { 
    typedef pack< int, double, char > pack1; 
    typedef pack< double, char > pack2; 

    typedef filter_seq< filter_zero_out, typename get_indexes<pack1>::type >::type reindex; 
    typedef filter_seq< filter_zero_out_b, typename get_indexes<pack1>::type >::type reindex_b; 

    typedef typename remap_pack< reindex, pack1 >::type pack2_clone; 
    typedef typename remap_pack< reindex_b, pack1 >::type pack2_clone_b; 

    std::cout << std::is_same< pack2, pack2_clone >::value << "\n"; 
    std::cout << std::is_same< pack2, pack2_clone_b >::value << "\n"; 
} 

Здесь мы имеем тип pack, который содержит произвольный список типов. См. Аккуратный ответ @LucTouraille о том, как переместиться между tuple и pack.

seq содержит последовательность указателей. remap_pack занимает seq и pack, а также создает pack, захватывая n-й элемент оригинала pack.

filter_seq принимает template<size_t> функтор и seq, и использует функтор для фильтрации элементов seq. Функтор может возвращать либо ::value типа size_t, либо ::result типа seq<...>, либо ни один из них, позволяющий использовать один-на-один или один-ко-многим функторы.

Несколько других вспомогательных функций, таких как conc, append, conc_pack, get_indexes, make_seq, nth_type круглых вещи.

Я тестировал его с filter_zero_out, который является на основе фильтра ::value, который удаляет 0, и filter_zero_out_b, который является на основе фильтра ::result, который также удаляет 0.

Смежные вопросы