Std:: tuple_element требуется глубокое создание шаблона

здесь 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: Почему С++ 11 не был добавлен специальный оператор для получения элементов по индексу? как кортеж [2] или кортеж [0]?

Q2: Возможно ли уменьшение глубинного экземпляра? Например, в языке D более шаблонные алгоритмы (в typetuple) нуждаются в O (log (N)) глубоком экземпляре.

EDIT: Q1: Почему С++ 11 не был добавлен специальный оператор для получения элементов по индексу из вариативных шаблонов? подобный шаблон < class... T > struct index {typedef T [3] third_element;}

Ответ 1

Почему С++ 11 не был добавлен специальный оператор для получения элементов по индексу? как tuple 2 или tuple [0]?

Во-первых, потому что, даже если бы они это сделали, все равно работа была бы одинаковой: с рекурсией. Кортежи в основном представляют собой библиотечную функцию. Хотя они откидывались от языковых функций, таких как вариативные шаблоны, они были более или менее функциональными в С++ 98/03.

Во-вторых, это было бы невозможно. Не без очень сложного изменения языка.

Не понятно, что вы подразумеваете под tuple[2].

Если вы имеете в виду, что std::tuple<int, float, std::string>[2] должно каким-то образом перейти к typename std::string, значит, теперь вам нужно объяснить, почему это работает. Опять же, кортежи представляют собой библиотечную функцию, а не конструкцию языка. Таким образом, должна существовать некоторая языковая конструкция, в которой typename[integer] является допустимой конструкцией. Что это было бы и что бы это значило?

Если вы имеете в виду, что данный:

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

Мы должны иметь возможность получить строку с tpl[2], что несколько оттенков "не произойдет". С++ - это статически типизированный язык. Единственная причина, по которой std::get может уйти от того, что она делает, заключается в том, что целочисленный индекс не является параметром функции; это параметр шаблона. Это позволяет std::get<0> возвращать совершенно другой тип из std::get<2>. Это невозможно с operator[](int); эта функция всегда должна возвращать один и тот же тип.

Итак, теперь вам нужно иметь что-то вроде template<class T, int i> ... operator[](). И это было бы очень запутанным, потому что вы больше не можете делать tpl[runtimeValue] на этом типе (поскольку параметры шаблона должны быть значениями времени компиляции). Нет такого типа, где operator[] не может работать со значениями времени выполнения. Таким образом, вы создадите очень странный тип.

И даже тогда... ему все равно придется делать рекурсию, чтобы получить значение.

Возможно ли уменьшить глубину создания?

Вне времени компиляции (что не является необоснованной проблемой), какое это имеет значение? Порядочный inliner бросит большинство из них.

Как и во время компиляции, существуют нерекурсивные реализации различных функций std::tuple. Могут ли они сделать tuple_element нерекурсивно, я так не думаю. Эта реализация libС++, кажется, предполагает, что не может, несмотря на то, что сама реализация tuple не рекурсивно.

Ответ 2

Я думаю, что эта реализация имеет O (log (N)) глубину реализации; kudos до Xeo для трюка индексов O (log (N)) (модифицировано для использования std::size_t вместо unsigned).

Edit: я понял там другое, более простое и, вероятно, более быстрое (время компиляции), чтобы получить 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;
};

Ответ 3

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

   // GCC couldn't optimize sizeof..(i) , 
   //see http://stackoverflow.com/info/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;
};