Re-implementation of `std::tuple` for MCUs Background
I'm writing code for an 8-bit MCU with very limited RAM (1KiB) and program flash space (64KiB). I have a C++14 capable compiler with no standard library implementation available. This is a good enough excuse for me to do some recreational template programming. :)
In the project where I intend to use this I need to be able to create a densely packed, complex, configurable tuple of varying types and values. And I need this data structure to be stored in program space, so I need it to be usable as constexpr.
About the code
In the code below I have used uint8_t for the size type because the MCU has 8-bit words and an int (which has to be 16 bits to be standard compliant) thus is &quot;extended precision&quot; for my MCU and incurs speed and program size overhead. And frankly, with 1K of ram I'm not expecting to make tuples with more than 255 elements.
The code is put into a single file for your reviewing pleasure and uses std::forward which is technically not available on my target, but I intend to re-implement it as the next step. This code is written for PC first to be able to debug and test it.
I'm happy for any comments on suggestions on the code, but please bear in mind the target architecture.
tuple_test.cpp
#include &lt;iostream&gt;
#include &lt;type_traits&gt;

//#define PACKED_TUPLES

#ifdef PACKED_TUPLES
#define ATTRIBUTE_PACKED __attribute__((__packed__))
#else
#define ATTRIBUTE_PACKED
#endif

namespace xtd {
  namespace detail {

    template &lt;typename...&gt;
    struct ATTRIBUTE_PACKED pack;

    // Helper class to get a value from a pack. 
    // This is required as template functions cannot be
    // partially specialised.
    template &lt;int, typename...&gt;
    struct pack_get;

    template &lt;typename... types&gt;
    struct pack_get&lt;0, types...&gt; {
      static auto get(const pack&lt;types...&gt;&amp; pack) { return pack.value; }
    };

    template &lt;int i, typename first_type, typename second_type, typename... types&gt;
    struct pack_get&lt;i, first_type, second_type, types...&gt; {
      static auto get(const pack&lt;first_type, second_type, types...&gt;&amp; pack) {
        return pack_get&lt;i - 1, second_type, types...&gt;::get(pack.next);
      }
    };

    template &lt;typename type&gt;
    struct ATTRIBUTE_PACKED pack&lt;type&gt; {
      constexpr static int size() { return 1; }

      constexpr pack() = default;
      constexpr pack(type arg) : value(arg) {}

    private:
      const type value;

      template &lt;int, typename...&gt;
      friend struct pack_get;
    };

    template &lt;typename type, typename... types&gt;
    struct ATTRIBUTE_PACKED pack&lt;type, types...&gt; {
      using next_pack = pack&lt;types...&gt;;

      constexpr pack() = default;
      constexpr pack(const type&amp; arg, types&amp;&amp;... args)
          : value(arg), next(std::forward&lt;types&gt;(args)...) {}

      constexpr static int size() { return 1 + next_pack::size(); }

    private:
      const type value;
      const next_pack next;

      template &lt;int, typename...&gt;
      friend struct pack_get;
    };
  }  // namespace detail

  template &lt;typename... types&gt;
  using tuple = detail::pack&lt;types...&gt;;

  template &lt;int i, typename... types&gt;
  auto get(const tuple&lt;types...&gt;&amp; tuple) {
    return detail::pack_get&lt;i, types...&gt;().get(tuple);
  }

  template &lt;typename... types&gt;
  constexpr auto make_tuple(types&amp;&amp;... args) {
    return tuple&lt;types...&gt;(std::forward&lt;types&gt;(args)...);
  }
}  // namespace xtd

extern constexpr auto p = xtd::make_tuple(3, 3.14, &quot;fooo&quot;);

int main(int, char**) {
  std::cout &lt;&lt; sizeof(p) &lt;&lt; std::endl;
  std::cout &lt;&lt; p.size() &lt;&lt; std::endl;
  auto x = xtd::get&lt;2&gt;(p);
  std::cout &lt;&lt; x &lt;&lt; std::endl;
}
Good stuff!
improvements:
Your constructors should move the value.
constexpr pack(type arg) : value(arg) {}

vs
constexpr pack(type arg) : value(std::move(arg)) {}

Inconsistent constructor parameter:
Your pack&lt;...&gt; template's constructor's first argument is by reference, whereas your root template takes it by value. Be consistent, pick one. (the one by value with a move is the correct choice here)
This is not actually forwarding:
constexpr pack(const type&amp; arg, types&amp;&amp;... args)
          : value(arg), next(std::forward&lt;types&gt;(args)...) {}

Forwarding is needed when the template deduction can deduce the type of a parameter to be a reference. I.E. the arguments themselves need to be templated. That's not the case here.
You can simply use:
constexpr pack(type arg, types... args)
          : value(std::move(arg)), next(std::move(args)...) {}

Note that your make_tuple() function DOES perform forwarding, so that one is fine.
Get by reference, please.
Your get function returns by value, when a reference would make more sense:
auto get(const tuple&lt;types...&gt;&amp; tuple) {}

vs
const auto&amp; get(const tuple&lt;types...&gt;&amp; tuple) {}
auto&amp; get(tuple&lt;types...&gt;&amp; tuple) {}

make_tuple() should decay the type of its arguments.
Specifically, you should apply std::decay on a per-arg basis so that make_tuple(int&amp;, const float&amp;) returns a tuple&lt;int, float&gt;.
No EBO
Since you seem to care about packing, you should really be performing empty base class optimisation to get the maximum oomph out of your tuple class. This does make the code a lot more complicated though, so if you know for a fact that you'll never pack 0-sized types in a tuple, then I wouldn't bother.
A note on PACKED_TUPLES:
Making this a compile-time switch like this is pretty dicey, as you can eventually run into some nasty binary incompatibilities in the future. I would rather make a complete separate class called packed_tuple&lt;&gt;, and use the compile-time switch in higher-level code so that symbols related to tuple&lt;&gt; and packed_tuple&lt;&gt; remain consistent between binaries. It also allows you to mix packed and unpacked tuples in the same binary which is potentially convenient, since __attribute__((__packed__)) can have a substantial hit on performance.