get part of std::tuple

前端 未结 7 1186
没有蜡笔的小新
没有蜡笔的小新 2020-12-02 23:09

I have a tuple of unknown size (it\'s template parametr of method)

Is it way to get part of it (I need throw away first element of it)

For example, I have

相关标签:
7条回答
  • 2020-12-02 23:47

    With help of a compile-time integer list:

    #include <cstdlib>
    
    template <size_t... n>
    struct ct_integers_list {
        template <size_t m>
        struct push_back
        {
            typedef ct_integers_list<n..., m> type;
        };
    };
    
    template <size_t max>
    struct ct_iota_1
    {
        typedef typename ct_iota_1<max-1>::type::template push_back<max>::type type;
    };
    
    template <>
    struct ct_iota_1<0>
    {
        typedef ct_integers_list<> type;
    };
    

    We could construct the tail simply by parameter-pack expansion:

    #include <tuple>
    
    template <size_t... indices, typename Tuple>
    auto tuple_subset(const Tuple& tpl, ct_integers_list<indices...>)
        -> decltype(std::make_tuple(std::get<indices>(tpl)...))
    {
        return std::make_tuple(std::get<indices>(tpl)...);
        // this means:
        //   make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...)
    }
    
    template <typename Head, typename... Tail>
    std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& tpl)
    {
        return tuple_subset(tpl, typename ct_iota_1<sizeof...(Tail)>::type());
        // this means:
        //   tuple_subset<1, 2, 3, ..., sizeof...(Tail)-1>(tpl, ..)
    }
    

    Usage:

    #include <cstdio>
    
    int main()
    {
        auto a = std::make_tuple(1, "hello", 7.9);
        auto b = tuple_tail(a);
    
        const char* s = nullptr;
        double d = 0.0;
        std::tie(s, d) = b;
        printf("%s %g\n", s, d);
        // prints:   hello 7.9
    
        return 0;
    }
    

    (On ideone: http://ideone.com/Tzv7v; the code works in g++ 4.5 to 4.7 and clang++ 3.0)

    0 讨论(0)
  • 2020-12-02 23:47

    The c++17 way:

    #include <tuple>
    #include <string>
    
    template <size_t __begin, size_t...__indices, typename Tuple>
    auto tuple_slice_impl(Tuple &&tuple, std::index_sequence<__indices...>) {
      return std::make_tuple(std::get<__begin + __indices>(std::forward<Tuple>(tuple))...);
    }
    
    template <size_t __begin, size_t __count, typename Tuple>
    auto tuple_slice(Tuple &&tuple) {
      static_assert(__count > 0, "splicing tuple to 0-length is weird...");
      return tuple_slice_impl<__begin>(std::forward<Tuple>(tuple), std::make_index_sequence<__count>());
    }
    
    template <size_t __begin, size_t __count, typename Tuple>
    using tuple_slice_t = decltype(tuple_slice<__begin, __count>(Tuple{}));
    
    using test_tuple = std::tuple<int, int, bool, nullptr_t, std::string>;
    using sliced_test = tuple_slice_t<2, 2, test_tuple>;
    
    static_assert(std::tuple_size_v<sliced_test> == 2);
    static_assert(std::is_same_v<std::decay_t<decltype(std::get<0>(sliced_test{}))>, bool>);
    static_assert(std::is_same_v<std::decay_t<decltype(std::get<1>(sliced_test{}))>, nullptr_t>);
    
    #include <cassert>
    
    int main() {
      test_tuple tuple {
        -1, 42, true, nullptr, "hello"
      };
      auto spliced = tuple_slice<3, 2>(tuple);
      assert(std::get<0>(spliced) == nullptr);
      assert(std::get<1>(spliced) == std::get<4>(tuple));
      return 0;
    }
    
    
    0 讨论(0)
  • 2020-12-02 23:51

    A tuple slice operation (that also works for std::array and std::pair) can be defined like this (C++14 required):

    namespace detail
    {
        template <std::size_t Ofst, class Tuple, std::size_t... I>
        constexpr auto slice_impl(Tuple&& t, std::index_sequence<I...>)
        {
            return std::forward_as_tuple(
                std::get<I + Ofst>(std::forward<Tuple>(t))...);
        }
    }
    
    template <std::size_t I1, std::size_t I2, class Cont>
    constexpr auto tuple_slice(Cont&& t)
    {
        static_assert(I2 >= I1, "invalid slice");
        static_assert(std::tuple_size<std::decay_t<Cont>>::value >= I2, 
            "slice index out of bounds");
    
        return detail::slice_impl<I1>(std::forward<Cont>(t),
            std::make_index_sequence<I2 - I1>{});
    }
    

    And an arbitrary subset of a tuple t can be obtained like so :

    tuple_slice<I1, I2>(t); 
    

    Where [I1, I2) is the exclusive range of the subset and the return value is a tuple of either references or values depending on whether the input tuple is an lvalue or an rvalue respectively (a thorough elaboration can be found in my blog).

    0 讨论(0)
  • 2020-12-02 23:57

    Please don't use!

    • This is [most probably] unspecified behaviour. It could stop working at any time.
    • Furthermore, there is a possibility of padding issues (i.e. it may work for int but may fail for your type!).

    See comments for discussion. I'm leaving this answer just for reference.


    Even simpler:

    tuple<int,int,int> origin{7,12,42};
    tuple<int, int> &tail1 = (tuple<int, int>&)origin;
    tuple<int> &tail2 = (tuple<int>&)origin;
    cout << "tail1: {" << get<0>(tail1) << ", " << get<1>(tail1) << "}" << endl;
    cout << "tail2: {" << get<0>(tail2) << "}" << endl;
    

    I got:

    tail1: {12, 42}
    tail2: {42}
    

    I'm not certain that this is not an unspecified behaviour. Works for me: Fedora 20 and

    ❯ clang --version
    clang version 3.3 (tags/RELEASE_33/final)
    Target: x86_64-redhat-linux-gnu
    Thread model: posix
    ❯ gcc --version
    gcc (GCC) 4.8.2 20131212 (Red Hat 4.8.2-7)
    Copyright (C) 2013 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    

    References: article on voidnish.wordpress.com/.

    0 讨论(0)
  • 2020-12-03 00:07

    With C++17, you can use std::apply:

    template <typename Head, typename... Tail>
    std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& t)
    {
        return apply([](auto head, auto... tail) {
            return std::make_tuple(tail...)};
        }, t);
    }
    
    0 讨论(0)
  • 2020-12-03 00:07

    I made some modifications to Adam's code that would strip off the first N arguments of the tuple, as well as create a new tuple with only the last N types ... Here is the complete code (note: if anyone decides to +1 my answer, also please +1 Adam's answer since that is what this code is based on, and I don't wish to take any credit away from his contribution):

    //create a struct that allows us to create a new tupe-type with the first
    //N types truncated from the front
    
    template<size_t N, typename Tuple_Type>
    struct tuple_trunc {};
    
    template<size_t N, typename Head, typename... Tail>
    struct tuple_trunc<N, std::tuple<Head, Tail...>>
    {
        typedef typename tuple_trunc<N-1, std::tuple<Tail...>>::type type;
    };
    
    template<typename Head, typename... Tail>
    struct tuple_trunc<0, std::tuple<Head, Tail...>>
    {
        typedef std::tuple<Head, Tail...> type;
    };
    
    /*-------Begin Adam's Code-----------
    
    Note the code has been slightly modified ... I didn't see the need for the extra
    variadic templates in the "assign" structure.  Hopefully this doesn't break something
    I didn't forsee
    
    */
    
    template<size_t N, size_t I>
    struct assign 
    {
        template<class ResultTuple, class SrcTuple>
        static void x(ResultTuple& t, const SrcTuple& tup) 
        {
            std::get<I - N>(t) = std::get<I>(tup);  
            assign<N, I - 1>::x(t, tup);  //this offsets the assignment index by N
        }
    };
    
    template<size_t N>
    struct assign<N, 1> 
    {
        template<class ResultTuple, class SrcTuple>
        static void x(ResultTuple& t, const SrcTuple& tup) 
        {
            std::get<0>(t) = std::get<1>(tup);
        }
    };
    
    
    template<size_t TruncSize, class Tup> struct th2;
    
    //modifications to this class change "type" to the new truncated tuple type
    //as well as modifying the template arguments to assign
    
    template<size_t TruncSize, class Head, class... Tail>
    struct th2<TruncSize, std::tuple<Head, Tail...>> 
    {
        typedef typename tuple_trunc<TruncSize, std::tuple<Head, Tail...>>::type type;
    
        static type tail(const std::tuple<Head, Tail...>& tup) 
        {
            type t;
            assign<TruncSize, std::tuple_size<type>::value>::x(t, tup);
            return t;
        }
    };
    
    template<size_t TruncSize, class Tup>
    typename th2<TruncSize, Tup>::type tail(const Tup& tup) 
    {
        return th2<TruncSize, Tup>::tail(tup);
    }
    
    //a small example
    int main()
    {
        std::tuple<double, double, int, double> test(1, 2, 3, 4);
        tuple_trunc<2, std::tuple<double, double, int, double>>::type c = tail<2>(test);
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题