This can be done if the tuple type arguments are homogeneous. (We can't sort non-homogeneous types because that would require rearrangement of the types themselves, and that's not something you can do at compile time.1)
Assuming homogeneous types, the solution basically boils down to this:
- Throw the arguments into an array.
- Sort the array.
- Make a tuple from the array contents.
This isn't too hard. First we need the indices trick to index our array (for step 3 -- you can use std::index_sequence
instead if you have C++14):
template <std::size_t... Is>
struct indices {};
template <std::size_t N, std::size_t... Is>
struct build_indices
: build_indices<N-1, N-1, Is...> {};
template <std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};
Then we need a way to peel off the first type from the parameter pack to declare our array (for step 1). As a bonus, we'll have it check to make sure all the types are the same:
template <typename...>
struct pack_type;
template <typename Head>
struct pack_type<Head>
{
using type = Head;
};
// Will fail deduction on a non-homogeneous pack.
template <typename Head, typename... Tail>
struct pack_type<Head, Head, Tail...> : pack_type<Head, Tail...> {};
Finally, our sorter implementation with a helper to build the indices pack:
template <std::size_t... I, typename Comparer, typename... Ts>
std::tuple<Ts...> make_sorted_tuple_impl(indices<I...>, Comparer const &c, Ts && ...args)
{
typename pack_type<Ts...>::type values[sizeof...(Ts)] = { std::forward<Ts>(args)... };
std::sort(std::begin(values), std::end(values), c);
return std::make_tuple(std::forward<Ts>(values[I])...);
}
// Special case to handle empty tuples.
template <typename Comparer>
std::tuple<> make_sorted_tuple_impl(indices<>, Comparer const &)
{
return std::tuple<>();
}
template <typename Comparer, typename... Ts>
std::tuple<Ts...> make_sorted_tuple(Comparer const &c, Ts && ...args)
{
return make_sorted_tuple_impl(build_indices<sizeof...(Ts)>(), c, std::forward<Ts>(args)...);
}
See it run.
Also please explain the typename __decay_and_strip<_Elements>::__type...
and _Elements&&...
bits...
I'm not going to explain the first because identifiers containing __
are reserved by the C++ implementation, so this __decay_and_strip
is an implementation detail specific to this particular C++ implementation.
_Elements&&...
is a pack of rvalue references. This allows the arguments to be perfectly forwarded to the std::tuple
constructor.
1 I lied. You can do it if the values and the compare function are constexpr
, but the code to pull it off will be huge and not worth the time to write.