The big problem with your nested tuple is that you want to have a field of type four_tuple<char,double,char,double>::payload aligned just as if it were four_tuple<char,double,char,double> , but not requiring the container type to inherit its alignment. It's complicated. Doing this is possible, but it makes your code very incapable of anything other than GCC. I assume that everything is in order as you already offer the GCC extension in your question. The basic idea is that bit fields can be used to insert gaskets to ensure alignment:
struct __attribute__((packed)) S { char c; // at offset 0 int i; // at offset 1, not aligned int : 0; int j; // at offset 8, aligned int : 0; int k; // at offset 12, no extra padding between j and k };
int , of course, a very specific type with very specific alignment, and you need dynamically defined alignment. Fortunately, GCC allows char -type bit fields, which usually only provide byte alignment, which should be combined with alignas , guaranteeing arbitrary alignment.
With this, you can check all 24 possible field orders and select the payload that gives the lowest overall size. I made the payload a global type and gave it an additional template parameter to indicate the order of the fields. This allows tuple4<T1, T2, T3, T4> to check tuple4_payload<T1, T2, T3, T4, 1234> , tuple4_payload<T1, T2, T3, T4, 1243> etc. In order and choose depending on which is better.
template <typename...> struct smallest; template <typename...T> using smallest_t = typename smallest<T...>::type; template <typename T> struct smallest<T> { using type = T; }; template <typename T, typename...Ts> struct smallest<T, Ts...> { using type = std::conditional_t<sizeof(T) <= sizeof(smallest_t<Ts...>), T, smallest_t<Ts...>>; }; template <typename T1, typename T2, typename T3, typename T4> struct tuple4; template <typename T1, typename T2, typename T3, typename T4, int fieldOrder> struct tuple4_payload; template <typename T1, typename T2, typename T3, typename T4> struct tuple4_simple { T1 t1; T2 t2; T3 t3; T4 t4; }; template <typename T> struct extract_payload { using type = T; }; template <typename...T> struct extract_payload<tuple4<T...>> { using type = typename tuple4<T...>::payload; }; template <typename T> using extract_payload_t = typename extract_payload<T>::type; #define PERMS \ PERM(1,2,3,4) PERM(1,2,4,3) PERM(1,3,2,4) PERM(1,3,4,2) PERM(1,4,2,3) PERM(1,4,3,2) \ PERM(2,1,3,4) PERM(2,1,4,3) PERM(2,3,1,4) PERM(2,3,4,1) PERM(2,4,1,3) PERM(2,4,3,1) \ PERM(3,1,2,4) PERM(3,1,4,2) PERM(3,2,1,4) PERM(3,2,4,1) PERM(3,4,1,2) PERM(3,4,2,1) \ PERM(4,1,2,3) PERM(4,1,3,2) PERM(4,2,1,3) PERM(4,2,3,1) PERM(4,3,1,2) PERM(4,3,2,1) #define PERM(a,b,c,d) \ template <typename T1, typename T2, typename T3, typename T4> \ struct __attribute__((packed)) tuple4_payload<T1, T2, T3, T4, a##b##c##d> { \ char : 0 alignas(T##a); extract_payload_t<T##a> t##a; \ char : 0 alignas(T##b); extract_payload_t<T##b> t##b; \ char : 0 alignas(T##c); extract_payload_t<T##c> t##c; \ char : 0 alignas(T##d); extract_payload_t<T##d> t##d; \ }; PERMS #undef PERM #define PERM(a,b,c,d) , tuple4_payload<T1, T2, T3, T4, a##b##c##d> template <typename, typename...T> using tuple4_smallest_payload_t = smallest_t<T...>; template <typename T1, typename T2, typename T3, typename T4> struct alignas(tuple4_simple<T1, T2, T3, T4>) tuple4 : tuple4_smallest_payload_t<void PERMS> { using payload = tuple4_smallest_payload_t<void PERMS>; }; #undef PERM
In your case, you would use this as tuple4<int, tuple4<char, double, char, double>, int, int> . Note that although the payload type is not explicitly mentioned here, it will still be used for the t2 member.