首页 文章

删除第一种类型的std :: tuple

提问于
浏览
24

这似乎是一个非常简单的问题:如何删除_3018486中的第一个(第n个)类型?

例:

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

上述操作会将 tuple1 转换为 tuple2 . 可能吗?

4 回答

  • 3

    您可以使用基于类模板的部分特化的简单类型函数:

    #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-th 类型:

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

    我写了一个proposal,它被C 14标准所接受,这使得任何"tuple-like"类型都很容易做到,即支持 tuple_sizetuple_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) ,其中 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

  • 28

    我提出了一个非常类似于@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

    对于此任务,这是一个过度设计的 template 元编程 . 它包括通过过滤器 templatetuple 的类型进行任意重新排序/重复/删除的能力:

    #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关于如何在 tuplepack 之间移动的简洁答案 .

    seq 包含一系列索引 . remap_pack 采用 seqpack ,并通过抓取原始 pack 的第n个元素构建结果 pack .

    filter_seq 采用 template<size_t> 仿函数和 seq ,并使用仿函数过滤 seq 的元素 . 仿函数可以返回 ::value 类型的 ::valueseq<...> 类型的 ::result ,或者两者都不允许,允许一对一或一对多仿函数 .

    一些其他的辅助函数,如 concappendconc_packget_indexesmake_seqnth_type .

    我用 filter_zero_out 测试了它,这是一个基于 ::value 的过滤器,删除0,和 filter_zero_out_b 这是一个基于 ::result 的过滤器,也删除0 .

相关问题