2013-02-13 28 views
22

को हटाकर यह एक बहुत ही सरल सवाल प्रतीत होता है: std::tuple में पहला (एन-वें) प्रकार कैसे हटाता है?पहले प्रकार के std :: tuple

उदाहरण:

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

आपरेशन ऊपर वर्णित tuple2 में tuple1 परिणत हो गया। क्या यह संभव है?

#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!" 
     ); 
} 

इसके अलावा, इस समाधान आसानी से i-वें एक टपल के प्रकार के दूर करने के लिए सामान्यीकृत किया जा सकता:

+0

अधिभार' ऑपरेटर = ', है filter_zero_out साथ यह परीक्षण किया रों? –

+2

कार्यान्वयन की त्वरित रूपरेखा इसलिए उत्तर देने के लिए क्षमता * और समय * के साथ कोई ऐसा कर सकता है :): 0 के बिना इंडेक्स पैक करें, और विविधता '' 'make_tuple' पर बढ़ाएं। –

+0

@meh: यह काम नहीं करेगा क्योंकि मुझे वस्तुओं में नहीं, प्रकारों में दिलचस्पी है। – cschwan

उत्तर

26

आप एक वर्ग टेम्पलेट का आंशिक विशेषज्ञता के आधार पर एक सरल प्रकार समारोह का उपयोग कर सकते

#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

आह, आंशिक विशेषज्ञता! धन्यवाद, यह है! – cschwan

+0

+1, टेम्पलेट मेटा प्रोग्रामिंग बहुत अच्छा है :) – StoryTeller

+1

@cschwan: आपका स्वागत है :-) मैंने एक tuple –

3

मैं @ एंडी द्वारा प्रस्तावित एक समाधान के साथ आया था, लेकिन यह std::tuple के बजाय पैरामीटर पैक (डमी रैपर का उपयोग करके) पर सीधे काम करके थोड़ा अधिक सामान्य होने का प्रयास करता है। इस तरह, आपरेशन के साथ-साथ अन्य variadic टेम्पलेट के लिए लागू किया जा सकता है, न केवल tuples रहे हैं:

#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 पैरामीटर पैक से एनएच प्रकार को हटाने के लिए आंशिक टेम्पलेट विशेषज्ञता का उपयोग करता है, परिणाम को pack में संग्रहीत करता है। अंत में, remove_nth एक मनमाना वर्ग टेम्पलेट का एक विशेषज्ञ लेता है, एनएचटी प्रकार को अपने टेम्पलेट पैरामीटर से हटा देता है, और नई विशेषज्ञता वापस कर देता है।

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>> 
    { }; 

:

8

मैं एक proposal जो सी ++ 14 मानक यह काफी किसी भी "टपल की तरह" प्रकार के लिए क्या करने के लिए आसान बनाने, यानी एक tuple_size और tuple_element API का समर्थन करने को स्वीकार कर लिया लिखा था

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) बनाता है जहां Ntuple_size<T>::value है: और आपको कार्यों का केवल एक जोड़े के साथ नए प्रकार में एक टपल वस्तु बदल सकता है , फिर एक नया [1,2,...,N)

परीक्षण में I के लिए make_tuple(get<I>(t)...) साथ टपल बनाता है यह:

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' की आवश्यकता है, और मुझे लगता है कि मैं जा रहा हूं प्रस्तावित करें 'लागू() 'में जोड़ा जाना चाहिए)। मेरा ईमेल पता जीसीसी मेलिंग सूचियों से ढूंढना आसान है। –

+1

अच्छा प्रस्ताव। मैं शायद एक संकेत जोड़ दूंगा कि आप वास्तव में 'ओ (लॉग एन)' जटिलता में ऐसी सूची उत्पन्न कर सकते हैं। [यहां] देखें (http://stackoverflow.com/a/13073076/500104)। साथ ही संकलक लेखकों को अपनी अंतर्निहित कार्यक्षमता का उपयोग करना चाह सकता है (आईआईआरसी, जीसीसी और क्लैंग दोनों में '__builtin_make_indices' के समान कुछ है)। – Xeo

1

पर यह इस के लिए template metaprogramming के एक से अधिक इंजीनियर सा है कार्य।

#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 कि प्रकार के एक मनमाना सूची रखती है: यह एक फिल्टर template के माध्यम से मनमाने ढंग से पुन: क्रमित/दोहराव/एक tuple के प्रकार पर इन्हें हटाने ऐसा करने की क्षमता भी शामिल है। tuple और pack के बीच कैसे स्थानांतरित करें के लिए @LucTouraille का साफ जवाब देखें।

seq इंडेक्स का अनुक्रम रखता है। remap_packseq और pack लेता है, और मूल pack के nth तत्व को पकड़कर परिणामस्वरूप pack बनाता है।

filter_seq एक template<size_t> functor और एक seq लेता है, और seq के तत्वों फिल्टर करने के लिए functor उपयोग करता है। मज़ेदार ::value प्रकार size_t या ::result प्रकार seq<...> या न तो, एक-से-एक या एक से कई मल्टीकर्स को अनुमति दे सकता है।

कुछ अन्य सहायक काम करता है, की तरह conc, append, conc_pack, get_indexes, make_seq, nth_type दौर बातें बाहर।

मैं जो एक ::value आधारित फिल्टर कि 0 निकाल देता है, और filter_zero_out_b जो जो `tuple` इन दो प्रकार लेता है एक ::result आधारित फिल्टर वह भी निकाल देता है 0.

संबंधित मुद्दे