std :: tuple_element需要深度模板即时功能

时间:2013-09-03 13:02:10

标签: c++11 stdtuple

在这里http://en.cppreference.com/w/cpp/utility/tuple/tuple_element给出了std :: tuple_element的可能实现。

 template< std::size_t I, class T >
struct tuple_element;

// recursive case
template< std::size_t I, class Head, class... Tail >
struct tuple_element<I, std::tuple<Head, Tail...>>
    : std::tuple_element<I-1, std::tuple<Tail...>> { };

// base case
template< class Head, class... Tail >
struct tuple_element<0, std::tuple<Head, Tail...>> {
   typedef Head type;
};

但是,如果元组有很多参数(超过100或200个参数),这个实现需要深度递归实例化。

Q1:为什么C ++ 11没有添加特殊运算符来获取索引元素? 像元组[2]或元组[0]?

Q2:有可能减少深度实例化吗?例如,在D语言中,更多模板算法(在typetuple中)需要O(log(N))深度实例化。

编辑:Q1:为什么C ++ 11没有添加特殊运算符来从变量模板获取索引元素? 像模板&lt; class ... T&gt; struct index {typedef T [3] third_element;}

3 个答案:

答案 0 :(得分:6)

我认为这个实现有O(log(N))实例化深度;对于O(log(N))索引技巧感到荣幸Xeo(修改为使用std::size_t而不是unsigned)。

编辑:我意识到有一个不同的,更简单的,可能更快的(编译时)解决方案来获得元组的第n种类型。

// from https://stackoverflow.com/a/13073076
// indices trick in O(log(N)) instantiations, by Xeo

    // using aliases for cleaner syntax
    template<class T> using Invoke = typename T::type;

    template<std::size_t...> struct seq{ using type = seq; };

    template<class S1, class S2> struct concat;

    template<std::size_t... I1, std::size_t... I2>
    struct concat<seq<I1...>, seq<I2...>>
      : seq<I1..., (sizeof...(I1)+I2)...>{};

    template<class S1, class S2>
    using Concat = Invoke<concat<S1, S2>>;

    template<std::size_t N> struct gen_seq;
    template<std::size_t N> using GenSeq = Invoke<gen_seq<N>>;

    template<std::size_t N>
    struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};

    template<> struct gen_seq<0> : seq<>{};
    template<> struct gen_seq<1> : seq<0>{};

实施/ {类似于std::tuple_element

namespace detail
{
    template<std::size_t>
    struct Any
    {
        Any(...) {}
    };

    template<typename T>
    struct wrapper { using type = T; };

    template<std::size_t... Is>
    struct get_nth_helper
    {
        template<typename T>
        static auto deduce(Any<Is>..., wrapper<T>, ...) -> wrapper<T>;
    };

    template<std::size_t... Is, typename... Ts>
    auto deduce_seq(seq<Is...>, wrapper<Ts>... pp)
    -> decltype( get_nth_helper<Is...>::deduce(pp...) );
}

#include <tuple>

template<std::size_t n, class Tuple>
struct tuple_element;

template<std::size_t n, class... Ts>
struct tuple_element<n, std::tuple<Ts...>>
{
    using wrapped_type = decltype( detail::deduce_seq(gen_seq<n>{},
                                                      detail::wrapper<Ts>()...) );
    using type = typename wrapped_type::type;
};

用法示例:

#include <typeinfo>
#include <iostream>

int main()
{
    std::tuple<int, double, bool, char> t;
    tuple_element<1, decltype(t)>::type x;
    std::cout << typeid(x).name() << std::endl;
}

感谢@Barry在函数/数组类型的本答案的早期版本中指出了一个问题,并提供了修复。


原始版本: (注意:此版本已简化,不添加cv限定符。)

#include <tuple>


namespace detail
{
    template < std::size_t Index, class Arg >
    struct s_get_one
    {
        // declare a function that links an Index with an Arg type
        friend Arg get(s_get_one, std::integral_constant<std::size_t, Index>);
    };

    template < typename... Bases >
    struct s_get : Bases... {};
}

template < std::size_t I, class T >
struct tuple_element;

template < std::size_t I, class... Args >
struct tuple_element < I, std::tuple<Args...> >
{
    template<class T>
    struct wrapper { using type = T; };

    // deduce indices from seq helper
    template < std::size_t... Is >
    static auto helper(seq<Is...>)
        -> detail::s_get< detail::s_get_one<Is, wrapper<Args>>... >;

    // generate indices in O(log(N)) and use name lookup to find the type
    using IC = std::integral_constant<std::size_t, I>;
    using wrapped_type = decltype( get(helper(gen_seq<sizeof...(Args)>{}), IC{}) );
    using type = typename wrapped_type::type;
};

答案 1 :(得分:5)

  

为什么C ++ 11没有添加特殊运算符来获取索引元素?像元组2或元组[0]?

首先,因为即使他们这样做了,它仍然以相同的方式工作:递归。元组主要是功能。虽然他们背负了可变参数模板等语言功能,但它们或多或少在C ++ 98/03中具有功能。

其次,是不可能的。不是没有非常困难的语言变化。

目前尚不清楚tuple[2]的含义。

如果您的意思是std::tuple<int, float, std::string>[2]应该以某种方式解析为类型名std::string,那么这意味着您现在需要解释其原因。同样,元组是特性,而不是语言构造。因此,必须有一些语言结构,typename[integer]是一个有效的结构。那将是什么以及它意味着什么?

如果你的意思是:

std::tuple<int, float, std::string> tpl{...};

我们应该能够获得tpl[2]的字符串,即几个阴影“不会发生”。 C ++是一种静态类型语言。 std::get能够逃脱它的唯一原因是整数索引不是函数参数;它是模板参数。这是允许std::get<0>std::get<2>返回完全不同的类型的原因。 operator[](int)不会发生这种情况;该函数必须始终返回相同的类型。

所以现在你需要像template<class T, int i> ... operator[]()这样的东西。这将非常令人困惑,因为您不能再对该类型执行tpl[runtimeValue](因为模板参数必须是编译时值)。没有这样的类型,operator[]被限制为无法处理运行时值。所以你要创建一个非常奇怪的类型。

即便如此...... 仍需要进行递归才能获得价值。

  

是否可以减少深度实例化?

在编译时间之外(这不是一个不合理的问题),这有什么关系?一个体面的内联将把大部分内容扔掉。

至于编译时间,std::tuple的各种功能有non-recursive implementations。他们是否可以非递归地tuple_element,我不这么认为。 This libc++ implementation似乎建议使用it can't,尽管非递归地实现了tuple本身。

答案 2 :(得分:3)

    template< int ...i> struct seq{};

   // GCC couldn't optimize sizeof..(i) , 
   //see http://stackoverflow.com/questions/19783205/why-sizeof-t-so-slow-implement-c14-make-index-sequence-without-sizeof
   //so I use direct variable `s` instead of it.
   // i.e.  s == number of variadic arguments in `I`.
    template< int s, typename I, typename J > struct concate;

    template< int s, int ...i, int ...j>
    struct concate<s, seq<i...>, seq<j...> >
    { 
        typedef seq<i..., (s  + j)...> type;
    };

    template<int n> struct make_seq_impl;
    template< int n> using make_seq = typename make_seq_impl<n>::type;

    template<> struct make_seq_impl<0>{ typedef seq<> type;};
    template<> struct make_seq_impl<1>{ typedef seq<0> type;};

    template<int n> struct make_seq_impl: concate< n/2, make_seq<n/2>, make_seq<n-n/2>>{};

    template< typename ...T> using seq_for = make_seq< sizeof...(T) > ;

//----------------------------------
template< int i, typename T> struct id{};
template< typename T> struct id<0,T>{ typedef T type;};
template< typename ...T> struct base : T ... {};

template< typename ...T> struct tuple{};

template< std::size_t i, typename Tuple> struct tuple_element;

template< std::size_t i, typename ...T>
struct tuple_element< i, tuple<T...> >
{
      template< typename Seq > struct apply;
      template< int ...j > struct apply< seq<j...> >
      {
         // j xor i ==> ( 0 xor i), (1 xor i), (2 xor i ),...(i xor i) ...
         //    =>  i0, i1, ..., 0 (at pos i) ...
         // and only id<0,T> has `type`.
          typedef base< id< (j xor i), T> ... > base_t;
          typedef typename base_t::type type;
       };

     typedef typename apply< seq_for<T...> >::type type;
};