Very basic tuple implementation I've been messing with metaprogramming and variadic templates in C++, and I came up with this very primitive implementation of a tuple:

constexpr bool GreaterThanZero(int N)
{
    return N &gt; 0;
}

template &lt;int, typename...&gt;
struct Helper;

template &lt;int N, typename Head, typename... Tail&gt;
struct Helper&lt;N, Head, Tail...&gt;
{
    typedef typename Helper&lt;N-1, Tail...&gt;::type type;
};

template &lt;typename Head, typename... Tail&gt;
struct Helper&lt;0, Head, Tail...&gt;
{
    typedef Head&amp; type;
};

template &lt;int, typename...&gt;
class TupleImpl;

template &lt;&gt;
class TupleImpl&lt;-1&gt;
{

};

template &lt;typename Head&gt;
class TupleImpl&lt;0, Head&gt;
{
protected:
    Head head;

public:
    template &lt;int Depth&gt;
    Head&amp; get()
    {
        static_assert(Depth == 0, "Requested member deeper than Tuple");
        return head;
    }

    template &lt;int Depth&gt;
    const Head&amp; get() const
    {
        static_assert(Depth == 0, "Requested member deeper than Tuple");
        return head;
    }
};

template &lt;int N, typename Head, typename... Tail&gt;
class TupleImpl&lt;N, Head, Tail...&gt;
{
protected:
    Head head;
    TupleImpl&lt;N-1, Tail...&gt; tail;



public:
    template &lt;int M&gt;
    typename std::enable_if&lt;M == 0, Head&amp;&gt;::type get()
    {
        return head;
    }

    template &lt;int M&gt;
    typename std::enable_if&lt;GreaterThanZero(M), typename Helper&lt;M, Head, Tail...&gt;::type&gt;::type get()
    {
        return tail.get&lt;M-1&gt;();
    }

    template &lt;int M&gt;
    typename std::enable_if&lt;M == 0, const Head&amp;&gt;::type get() const
    {
        return head;
    }

    template &lt;int M&gt;
    typename std::enable_if&lt;GreaterThanZero(M), typename Helper&lt;M, Head, Tail...&gt;::type&gt;::type get() const
    {
        return tail.get&lt;M-1&gt;();
    }
};

template &lt;typename... Elements&gt;
class Tuple : public TupleImpl&lt;sizeof...(Elements)-1, Elements...&gt;
{
public:
    static constexpr std::size_t size()
    {
        return sizeof...(Elements);
    }
};

int main()
{
    using namespace std;

    Tuple&lt;int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int&gt; test;
    Tuple&lt;&gt; test2;

    test.get&lt;0&gt;() = 1;
    test.get&lt;1&gt;() = 2;

    cout &lt;&lt; test.size() &lt;&lt; endl;
    cout &lt;&lt; test.get&lt;0&gt;() &lt;&lt; endl;
    cout &lt;&lt; test.get&lt;1&gt;() &lt;&lt; endl;
}


Being template metaprogramming, of course it looks terrible. Are there any ways I can clean it up? I've been picking up the nitty-gritty details of how templates work by messing around with stuff like this, but I'm sure I'm missing something that would simplify this.
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!