如何获得元组的一部分?

时间:2013-07-06 23:52:46

标签: c++ templates c++11 tuples

如何获取给定元组类型中第一个元素的元组类型?

如果我只要求一个元素,它应该给我内部类型而不是一个元素的元组类型。

在代码中,我如何获得type我正在寻找?

//TupleType is a tuple of n types 0,1,2,...,n-1
template<size_t i, typename TupleType>
struct first_elements {
  using type = //the "sub-tuple" of types 0,1,2,...,i
               //exception: if i=0, just the bare type within
               //the tuple, not the tuple of one element
};

完成后,我可以像这样使用它:

using my_tuple_type = first_elements<1,decltype(great_tuple)>::type;
using not_tuple_type = first_elements<0,decltype(great_tuple)>::type;

3 个答案:

答案 0 :(得分:2)

我几乎没有测试,但想法应该没问题:

很长但很直接

#include <tuple>
#include <iostream>



template<int n, typename In, typename... Out>
struct first_impl;

template<int n, typename First, typename... Other, typename... Out>
struct first_impl<n, std::tuple<First, Other...>, Out...> {
    typedef typename first_impl<n - 1, std::tuple<Other...>, Out..., First>::type type; //move first input to output.
};

//need First, Other... here to resolve ambiguity on n = 0
template<typename First, typename... Other, typename... Out>
struct first_impl<0, std::tuple<First, Other...>, Out...> {
    typedef typename std::tuple<Out...> type;  //stop if no more elements needed
};

//explicit rule for empty tuple because of First, Other... in the previous rule.
// actually it's for n = size of tuple
template<typename... Out>
struct first_impl<0, std::tuple<>, Out...> {
    typedef typename std::tuple<Out...> type; 
};



//Simple check
int main() {
    typedef first_impl<2, std::tuple<char, long, int>>::type type;
    std::cout << std::is_same<type, std::tuple<char, long>>::value; // 1
    return 0;
}

之后你需要使异常工作:

template<int n, typename T>
class first_elements<n, T> {
    typedef typename first_impl<n, T>:: type type;
}

template<typename First, typename... Other>
class first_elements<0, std::tuple<First, Other...>> {
    typedef First type;
}

template<>
class first_elements<0, std::tuple<>>; // make it incomplete

答案 1 :(得分:1)

使用tuple_element

std::tuple_element<0, TupleType>::type

与标准索引包技术一起使用,您也可以使用它来创建子数,例如std::tuple<typename std::tuple_element<Indices, TupleType>::type...>

答案 2 :(得分:1)

<强>更新

如果您在这里寻找将提取指定的代码 给定std::tuple的子序列和/或定义子序列的类型 给定的std::tuple - 类型,跳到 Anwser到明显的问题。 OP的问题实际上不是那个问题。

回答实际问题

这样做:

#include <cstddef>
#include <type_traits>
#include <tuple>

template<size_t I, typename TupleType, typename Enable = void>
struct first_elements;

template<size_t I, typename TupleType>
struct first_elements<I,TupleType,typename std::enable_if<(I == 0)>::type> 
{
    using type = typename std::tuple_element<0,TupleType>::type;    
};

template<size_t I, typename TupleType>
struct first_elements<I,TupleType,typename std::enable_if<(I == 1)>::type> 
{
    using type = 
    typename std::tuple<typename std::tuple_element<0,TupleType>::type>;        
};

template<size_t I, typename TupleType>
struct first_elements<I,TupleType,typename std::enable_if<(I > 1)>::type> 
{
    using next = 
    typename std::tuple<typename std::tuple_element<I - 1,TupleType>::type>;
    using prev = typename first_elements<I - 1,TupleType>::type;
    using type = decltype(
        std::tuple_cat(std::declval<prev>(),std::declval<next>())
    );
};

// A test program...

#include <iostream>

using namespace std;

int main(int argc, char **argv)
{
    auto tup = std::tuple<char,int,float,double>();
    cout << 
    std::is_same<first_elements<0,decltype(tup)>::type,char>::value << endl;
    cout << 
    std::is_same<first_elements<1,decltype(tup)>::type,std::tuple<char>>::value 
    << endl;
    cout << 
    std::is_same<
        first_elements<2,decltype(tup)>::type,
        std::tuple<char,int>>::value 
    << endl;
    cout << 
    std::is_same<
        first_elements<3,decltype(tup)>::type,
        std::tuple<char,int,float>>::value 
    << endl;
    cout << 
    std::is_same<
        first_elements<4,decltype(tup)>::type,
        std::tuple<char,int,float,double>>::value 
    << endl;
    return 0;
}

编译时范围错误没有特别的预防措施。他们将barf as std::tuple让他们成为barf。

(gcc 4.7.2 / 4.8.1.clang 3.3,-std=c++11

回答明显的问题

#include <type_traits>
#include <tuple>

/*  A class template to obtain the type and value of the
    the subsequence [First,Last) of a tuple type TupleType

    First:- The start of the subsequence [First,Last)
    Last:- The end of the subsequence [First,Last)
    TupleType: - The std::tuple type to be queried.
    Enable:- SFINAE parameter

    The public member `type` is defined as the type of the
    subsequence [First,Last) of `TupleType`.

    The static member function:

    `type get(TupleType const & tup)`

    returns the `std::tuple` of the subsequence [First,Last) of `tup`.
    `std::tuple<>` is returned when `First`
    is out of range. The terminal sub-tuple indexed by `First` is
    returned if only `Last` is out of range.

*/
template<
    unsigned First, unsigned Last, typename TupleType, typename Enable = void
>
struct tuple_part;

template<unsigned First, unsigned Last, typename TupleType>
struct tuple_part<
    First,Last,TupleType,
    typename std::enable_if<
        (First >= Last || First >= std::tuple_size<TupleType>::value)
    >::type
> 
{
    using type = std::tuple<>;

    static constexpr type get(TupleType const & tup) {
        return type();
    }
};

template<unsigned First, unsigned Last, typename TupleType>
struct tuple_part<
    First,Last,TupleType,
    typename std::enable_if<
        (Last == First + 1 && First < std::tuple_size<TupleType>::value)
    >::type
> 
{
    using type = 
    typename std::tuple<typename std::tuple_element<First,TupleType>::type>;

    static constexpr type get(TupleType const & tup) {
        return type(std::get<First>(tup));
    } 
};

template<unsigned First, unsigned Last, typename TupleType>
struct tuple_part<
    First,Last,TupleType,
    typename std::enable_if<
        (Last > First + 1 && Last <= std::tuple_size<TupleType>::value)
    >::type
>
{
    using head = typename tuple_part<First,First + 1,TupleType>::type;
    using tail = typename tuple_part<First + 1,Last,TupleType>::type;
    using type = decltype(
        std::tuple_cat(std::declval<head>(),std::declval<tail>())
    );

    static constexpr type get(TupleType const & tup) {
        return std::tuple_cat(
                    tuple_part<First,First + 1,TupleType>::get(tup),
                    tuple_part<First + 1,Last,TupleType>::get(tup)
                );
    }
};

template<unsigned First, unsigned Last, typename TupleType>
struct tuple_part<
    First,Last,TupleType,
    typename std::enable_if<
        (Last > First + 1 && Last > std::tuple_size<TupleType>::value)
    >::type
> : tuple_part<First,std::tuple_size<TupleType>::value,TupleType>
{
    using base_type = 
        tuple_part<First,std::tuple_size<TupleType>::value,TupleType>;
    using type = typename base_type::type;
};

/*
    `get_part<First,Last>(TupleType const & tup)`
    returns the `std::tuple` of the subsequence [First,Last) of `tup` 
*/ 
template<unsigned First, unsigned Last, typename TupleType>
constexpr
decltype(
    tuple_part<First,Last,TupleType>::get(std::declval<TupleType>())
)   
get_part(TupleType const & tup) 
{
    return tuple_part<First,Last,TupleType>::get(tup);
}

/*
    `get_part<First>(TupleType const & tup)`
    returns the `std::tuple` of the terminal subsequence of `tup`
    indexed by `First`
*/ 
template<unsigned First, typename TupleType>
constexpr
decltype(
    get_part<First,std::tuple_size<TupleType>::value>(std::declval<TupleType>())
)   
get_part(TupleType const & tup) 
{
    return get_part<First,std::tuple_size<TupleType>::value>(tup);
}

// A test program...

#include <cassert>

int main(int argc, char **argv)
{
    using type = std::tuple<char,int,float,double>;
    constexpr type t0(1,2,3.0,4.0);
    constexpr auto p0 = get_part<0,1>(t0);
    assert(p0 == std::tuple<char>(1));
    auto p1 = get_part<0,2>(t0);
    assert((p1 == std::tuple<char,int>(1,2)));
    auto p2 = get_part<0,3>(t0);
    assert((p2 == std::tuple<char,int,float>(1,2,3.0)));
    auto p3 = get_part<0>(t0);
    assert((p3 == std::tuple<char,int,float,double>(1,2,3.0,4.0)));
    auto p4 = get_part<1,2>(t0);
    assert(p4 == std::tuple<int>(2));
    auto p5 = get_part<1,3>(t0);
    assert((p5 == std::tuple<int,float>(2,3.0)));
    auto p6 = get_part<1>(t0);
    assert((p6 == std::tuple<int,float,double>(2,3.0,4.0)));
    auto p7 = get_part<2,3>(t0);
    assert(p7 == std::tuple<float>(3.0));
    auto p8 = get_part<2,4>(t0);
    assert((p8 == std::tuple<float,double>(3.0,4.0)));  
    auto p9 = get_part<3>(t0);
    assert(p9 == std::tuple<double>(4.0));
    auto p10 = get_part<3,5>(t0);
    assert(p10 == std::tuple<double>(4.0));
    auto p11 = get_part<4,4>(t0);
    assert(p11 == std::tuple<>());
    auto p12 = get_part<5,4>(t0);
    assert(p12 == std::tuple<>());  
    return 0;
}

// EOF

(gcc 4.7.2 / 4.8.1,clang 3.2 / 3.3,-std=c++11