Same approach as last time, O(logN) instantiation depth. Using only one overload, so it should consume less resources.
Warning: it currently removes references from the tuple types. Note: Removed the reference from pack::declval. I think it still works in every case.
indices trick in O(log(N)) instantiations, by Xeo; modified to use std::size_t instead of unsigned
#include <cstddef> // 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>{};
Today, I realized there's a different, simpler and probably faster (compilation time) solution to get the nth type of a tuple (basically an implementation of std::tuple_element). Even though it's a direct solution of another question, I'll also post it here for completeness.
namespace detail { template<std::size_t> struct Any { template<class T> Any(T&&) {} }; template<typename T> struct wrapper {}; template<std::size_t... Is> struct get_nth_helper { template<typename T> static T deduce(Any<Is>..., 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 type = decltype( detail::deduce_seq(gen_seq<n>{}, detail::wrapper<Ts>()...) ); };
Helper for last element:
template<typename Tuple> struct tuple_last_element; template<typename... Ts> struct tuple_last_element<std::tuple<Ts...>> { using type = typename tuple_element<sizeof...(Ts)-1, std::tuple<Ts...>> :: type; };
Usage example:
#include <iostream> #include <type_traits> int main() { std::tuple<int, bool, char const&> t{42, true, 'c'}; tuple_last_element<decltype(t)>::type x = 'c'; // it's a reference static_assert(std::is_same<decltype(x), char const&>{}, "!"); }
Original version:
#include <tuple> #include <type_traits> namespace detail { template<typename Seq, typename... TT> struct get_last_helper; template<std::size_t... II, typename... TT> struct get_last_helper< seq<II...>, TT... > { template<std::size_t I, std::size_t L, typename T> struct pack {}; template<typename T, std::size_t L> struct pack<L, L, T> { T declval(); }; // this needs simplification.. template<typename... TTpacked> struct exp : TTpacked... { static auto declval_helper() -> decltype(std::declval<exp>().declval()); using type = decltype(declval_helper()); }; using type = typename exp<pack<II, sizeof...(TT)-1, TT>...>::type; }; } template< typename Tuple > struct get_last; template< typename... TT > struct get_last<std::tuple<TT...>> { template<std::size_t... II> static seq<II...> helper(seq<II...>); using seq_t = decltype(helper(gen_seq<sizeof...(TT)>())); using type = typename detail::get_last_helper<seq_t, TT...>::type; }; int main() { using test_type = std::tuple<int, double, bool, char>; static_assert(std::is_same<char, get_last<test_type>::type>::value, "!"); // fails: static_assert(std::is_same<int, get_last<test_type>::type>::value, "!"); }