struct two_elements { int x; double y; }; struct five_elements { std::string one; std::unique_ptr<int> two; int * three; char four; std::array<two_elements, 10> five; }; struct anything { template<class T> operator T()const; }; namespace details { template<class T, class Is, class=void> struct can_construct_with_N:std::false_type {}; template<class T, std::size_t...Is> struct can_construct_with_N<T, std::index_sequence<Is...>, std::void_t< decltype(T{(void(Is),anything{})...}) >>: std::true_type {}; } template<class T, std::size_t N> using can_construct_with_N=details::can_construct_with_N<T, std::make_index_sequence<N>>; namespace details { template<std::size_t Min, std::size_t Range, template<std::size_t N>class target> struct maximize: std::conditional_t< maximize<Min, Range/2, target>{} == (Min+Range/2)-1, maximize<Min+Range/2, (Range+1)/2, target>, maximize<Min, Range/2, target> > {}; template<std::size_t Min, template<std::size_t N>class target> struct maximize<Min, 1, target>: std::conditional_t< target<Min>{}, std::integral_constant<std::size_t,Min>, std::integral_constant<std::size_t,Min-1> > {}; template<std::size_t Min, template<std::size_t N>class target> struct maximize<Min, 0, target>: std::integral_constant<std::size_t,Min-1> {}; template<class T> struct construct_searcher { template<std::size_t N> using result = ::can_construct_with_N<T, N>; }; } template<class T, std::size_t Cap=20> using construct_airity = details::maximize< 0, Cap, details::construct_searcher<T>::template result >;
This leads to a binary search for the longest harmony T from 0 to 20. 20 is a constant, you can increase it, like you, with compilation time and memory.
Living example .
If the data in your structure cannot be built from an rvalue of its own type, this will not work in C ++ 14, but I believe that guanteed elision is found in C ++ 17 here (!)
Including this in structured bindings requires more than a bit of a heap of manual code. But as soon as you do this, you should be able to ask questions such as "what is the 3rd type of this struct ", etc.
If a struct can be decomposed into structured bindings without tuple_size driven material, then it determines the number of variables that it needs.
Unfortunately std::tuple_size not SFINAE friendly even in C ++ 17. But for types that use the tuple_size part, ADL-enable std::get also required.
Create a namespace with failure_tag get<std::size_t>(Ts const&...) , which using std::get . Use this to determine if they have redefined get<0> in the type ( !std::is_same< get_type<T,0>, failure_tag >{} ), and if so, go the tuple_element path to determine the speed. Put the resulting elements in std::tuple of decltype(get<Is>(x)) and return it.
If this fails, use the construct_airity above and use this to figure out how to use structured bindings for the type. I would probably send this to std::tie for uniformity.
Now we have tuple_it that takes any structured binding and converts it into a tuple of links or values. Now both paths converge, and your common code is easier!