Very basic tuple implementation
Below is how I would clean this up, or maybe partially re-write [live example]:

// helpers
template &lt;typename T&gt;
struct id { using type = T; };

template &lt;typename T&gt;
using type_of = typename T::type;

template &lt;size_t... N&gt;
struct sizes : id &lt;sizes &lt;N...&gt; &gt; { };

// choose N-th element in list &lt;T...&gt;
template &lt;size_t N, typename... T&gt;
struct Choose;

template &lt;size_t N, typename H, typename... T&gt;
struct Choose &lt;N, H, T...&gt; : Choose &lt;N-1, T...&gt; { };

template &lt;typename H, typename... T&gt;
struct Choose &lt;0, H, T...&gt; : id &lt;H&gt; { };

template &lt;size_t N, typename... T&gt;
using choose = type_of &lt;Choose &lt;N, T...&gt; &gt;;

// given L&gt;=0, generate sequence &lt;0, ..., L-1&gt;
template &lt;size_t L, size_t I = 0, typename S = sizes &lt;&gt; &gt;
struct Range;

template &lt;size_t L, size_t I, size_t... N&gt;
struct Range &lt;L, I, sizes &lt;N...&gt; &gt; : Range &lt;L, I+1, sizes &lt;N..., I&gt; &gt; { };

template &lt;size_t L, size_t... N&gt;
struct Range &lt;L, L, sizes &lt;N...&gt; &gt; : sizes &lt;N...&gt; { };

template &lt;size_t L&gt;
using range = type_of &lt;Range &lt;L&gt; &gt;;

// single tuple element
template &lt;size_t N, typename T&gt;
class TupleElem
{
    T elem;
public:
    T&amp;       get()       { return elem; }
    const T&amp; get() const { return elem; }
};

// tuple implementation
template &lt;typename N, typename... T&gt;
class TupleImpl;

template &lt;size_t... N, typename... T&gt;
class TupleImpl &lt;sizes &lt;N...&gt;, T...&gt; : TupleElem &lt;N, T&gt;...
{
    template &lt;size_t M&gt; using pick = choose &lt;M, T...&gt;;
    template &lt;size_t M&gt; using elem = TupleElem &lt;M, pick &lt;M&gt; &gt;;

public:
    template &lt;size_t M&gt;
    pick &lt;M&gt;&amp; get() { return elem &lt;M&gt;::get(); }

    template &lt;size_t M&gt;
    const pick &lt;M&gt;&amp; get() const { return elem &lt;M&gt;::get(); }
};

template &lt;typename... T&gt;
struct Tuple : TupleImpl &lt;range &lt;sizeof...(T)&gt;, T...&gt;
{
    static constexpr std::size_t size() { return sizeof...(T); }
};


Comments:


With a bit more infrastructure (helper structs that are typically reused here and there) at the beginning, the main tuple implementation becomes just 20 lines.
Instead of a recursive implementation, I have switched to multiple (variadic) inheritance of single tuple elements TupleElem, each with its own id N (so that each element is of unique type) and its own function get(). Hence Tuple's function get() just redirects to the appropriate base class.
Now a specialization of TupleElem for empty types can bring easily the desired empty base optimization without affecting the main tuple implementation.
No specialization is needed for empty tuple. This definition includes empty tuple as a special case.
No static assertions needed, an out-of-range index will give an error anyway (but you can add for better messages of course).


Now, there are so many things missing. I would start from support for rvalue references and, of course, constructors. If you want to get an idea, you can have a look at my own tuple implementation (of which this one here is a miniature) including tuple views, expression templates for lazy evaluation, loops, algorithms, integration with all C++ operators, and much more.

Template metaprogramming does not have to look terrible!