A non-recursive tuple_element implementation This is basically a non-recursive std::tuple_element implementation.

Note

To make this non-recursive, you must replace std::make_index_sequence with a non-recursive implementation. I left it with std::make_index_sequence in order to reduce the amount of unrelated code.

How it works

deduct has a specialization of deduct_impl that is generated from the index sequence template argument it receives. It is used in order to deduce the type at index in a variadic type template or tuple. It uses the itp_base and itp types.

itp&lt;std::size_t&gt; and itp&lt;std::size_t, T&gt; is an index-type-pair used to expand the variadic indices template with a type variadic template in order to match the generated specialization.

deducer puts it all together by specializing deduct and deduct_impl by using std::conditional_t to generate the correct specialization.

Basically, for std::tuple&lt;void, int, char&gt;, in order to get the type at index 1, it creates itp_base&lt;0&gt;, itp&lt;1, int&gt;, itp_base&lt;2&gt; and passes it to deduct and deduct_impl.

Source code

#include &lt;utility&gt;
#include &lt;tuple&gt;

template &lt;std::size_t index&gt;
struct itp_base {};

template &lt;std::size_t index, typename T&gt;
struct itp : itp_base&lt;index&gt; {};

template &lt;std::size_t index, typename IndexSequence&gt;
struct deduct;

template &lt;std::size_t index, std::size_t... indices&gt;
struct deduct&lt;index, std::index_sequence&lt;indices...&gt;&gt;
{
    template &lt;typename Tuple&gt;
    struct deduct_impl;

    template &lt;typename T, typename... R&gt;
    struct deduct_impl&lt;std::tuple&lt;itp_base&lt;indices&gt;..., itp&lt;index, T&gt;, R...&gt;&gt;
    {
        using type = T;
    };
};

template &lt;std::size_t index, typename... Types&gt;
class deducer
{
private:
    static_assert( index &lt; sizeof...( Types ), "deducer::index out of bounds" );

    template &lt;typename IndexSequence&gt;
    struct deducer_impl;

    template &lt;std::size_t... indices&gt;
    struct deducer_impl&lt;std::index_sequence&lt;indices...&gt;&gt;
    {
        using type = typename deduct&lt;index, std::make_index_sequence&lt;index&gt;
        &gt;::template deduct_impl
        &lt;
            std::tuple
            &lt;
                std::conditional_t
                &lt;
                    std::is_base_of&lt;itp_base&lt;indices&gt;, itp&lt;index, Types&gt;&gt;::value,
                    itp&lt;index, Types&gt;,
                    itp_base&lt;indices&gt;
                &gt;...
            &gt;
        &gt;::type;
    };

public:
    using type = typename deducer_impl&lt;
        std::make_index_sequence&lt;sizeof...( Types )&gt;&gt;::type;
};


Convenience aliases

template &lt;std::size_t index, typename Tuple&gt;
struct tuple_element;

template &lt;std::size_t index, typename... Types&gt;
struct tuple_element&lt;index, std::tuple&lt;Types...&gt;&gt; : deducer&lt;index, Types...&gt; {};

template &lt;std::size_t index, typename... Types&gt;
using tuple_element_t = typename tuple_element&lt;index, Types...&gt;::type;


Test case

#include &lt;iostream&gt;
#include &lt;string&gt;

int main()
{
    using tuple_t = std::tuple&lt;int, void, std::string&gt;;
    static_assert( std::is_same&lt;tuple_element_t&lt;2, tuple_t&gt;, std::string&gt;::value, "!" );
}


Demo

http://coliru.stacked-crooked.com/a/16805356d2578d6d
Your approach on finding the nth element relies on being able to construct a tuple with the first n-1 types being some predictable thing that you can match against. But the types you chose for std::tuple&lt;void, int, char&gt; were, for index 1, std::tuple&lt;itp_base&lt;0&gt;, itp&lt;1, int&gt;, itp_base&lt;2&gt;&gt;. 

But we don't actually need to introduce this itp thing to do this, we can simply turn it into std::tuple&lt;void, int, void&gt;. That way, deduct_impl's specialization instead of taking a std::tuple&lt;itp_base&lt;indices&gt;..., itp&lt;index, T&gt;, R...&gt;, we can simplify to:

template &lt;std::size_t&gt;
using make_void = void;

template &lt;typename T, typename... R&gt;
struct deduct_impl&lt;std::tuple&lt;make_void&lt;indices&gt;..., T, R...&gt;&gt;
{
    using type = T;
};


And on the other side, we pass in:

std::tuple&lt;std::conditional_t&lt;(indices == index), Types, void&gt;...&gt;


Same idea, just more direct.

For that matter, we don't even need the tuple! std::tuple is an expensive type to construct at compile-time, so it's best to avoid it entirely. Just pass in the types as a pack (though the following doesn't compile on gcc - though even there you don't need tuple, just use a light wrapper like template &lt;class...&gt; struct typelist;)

template &lt;typename... Ts&gt;
struct deduct_impl;

template &lt;std::size_t&gt;
using make_void = void;

template &lt;typename T, typename... R&gt;
struct deduct_impl&lt;make_void&lt;indices&gt;..., T, R...&gt;
{
    using type = T;
};


and:

using type = typename deduct&lt;index, std::make_index_sequence&lt;index&gt;
&gt;::template deduct_impl
&lt;
    std::conditional_t&lt;(indices == index), Types, void&gt;...
&gt;::type;


A different approach

Another non-recursive way to do this would be through an inheritance tree. We basically take std::tuple&lt;void, int, char&gt; and turn it into a type that inherits from indexed&lt;0, void&gt;, indexed&lt;1, int&gt;, and indexed&lt;2, char&gt;:

template &lt;std::size_t I, typename T&gt;
struct indexed {
    using type = T;
};

template &lt;typename Is, typename ...Ts&gt;
struct indexer;

template &lt;std::size_t ...Is, typename ...Ts&gt;
struct indexer&lt;std::index_sequence&lt;Is...&gt;, Ts...&gt;
: indexed&lt;Is, Ts&gt;...
{};


Then, given a function template constrained on an index, we can pull out the correct type:

template &lt;std::size_t I, typename ...Ts&gt;
struct at_index {
private:
    template &lt;typename T&gt;
    static indexed&lt;I, T&gt; select(indexed&lt;I, T&gt;);

    using impl = indexer&lt;std::index_sequence_for&lt;Ts...&gt;, Ts...&gt;;
public:
    using type = typename decltype(select(impl{}))::type;
};


I don't know which solution is better, but this one is certainly shorter.