In a recent mail on the boost developers mailing list Eric Niebler mentioned that it is possible to get the last element of a template parameter pack in O(1)
instantiations.
How can this be done?
You can check https://github.com/ericniebler/proto-0x/blob/master/boost/proto/utility.hpp, as mentioned in the post, on how the get_nth
metafunctions are implemented. The essence, after much simplification:
#include <type_traits>
#include <cstdlib>
// A template to store a variadic list.
template <typename...>
struct List;
// Concatenate two variadic lists together. Should be O(1).
template <typename Left, typename Right>
struct Concat;
template <typename... Lefts, typename... Rights>
struct Concat<List<Lefts...>, List<Rights...>> {
typedef List<Lefts..., Rights...> Type;
};
// Construct List<void, void, void, ...> with 'n' elements.
// Uses "bisection" method, which should instantiate O(log n) templates
template <size_t n>
struct RepeatVoid : Concat<typename RepeatVoid<n/2>::Type,
typename RepeatVoid<n - n/2>::Type> {};
template <>
struct RepeatVoid<0> {
typedef List<> Type;
};
template <>
struct RepeatVoid<1> {
typedef List<void> Type;
};
template <typename>
struct GetNthAux;
// This would create a function of the form:
//
// T eval(cv void*, cv void*, cv void*, ..., cv void*, T*, ...)
//
// where there are 'n' const volatile void*. These will be used to absorb
// the first 'n' template arguments. The actual one can be extracted from
// the return type.
//
template <typename... Ts>
struct GetNthAux<List<Ts...>> {
template<typename T>
static constexpr T eval(const volatile Ts*..., T*, ...);
};
// Run the whole thing.
template<size_t n, typename... Ts>
using GetNth = decltype(
GetNthAux<typename RepeatVoid<n>::Type>::eval((Ts*)nullptr...)
);
// Example:
int main() {
static_assert(std::is_same<GetNth<0, int, float, char>, int>::value, "");
static_assert(std::is_same<GetNth<1, int, float, char>, float>::value, "");
static_assert(std::is_same<GetNth<2, int, float, char>, char>::value, "");
}
Actually it is O(log N), not O(1), because of the time taken in RepeatVoid
.
But this method does perform much better than linear methods like the typical implementation of tuple_element<>
. I have tried to compare the performance of GetNth
with tuple_element<>
on getting the item 899 of a list, and the latter is an order of magnitude faster:
tuple_element<> GetNth<>
g++ 4.7 with libstdc++ 0.7s 0.1s
clang++ 3.1 with libc++ 0.9s 0.1s
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With