3

Note:

My concern here is about compilation speeds.

I assume that recursive type traits are typically slower than the alternatives (when possible). If I'm wrong about this then please let me know.


We can access the types at the front of a variadic list without recursion, like so:

#include <iostream>
#include <type_traits>

template <typename T>
struct this_type
{
    using type = T;
};

template <typename T1, typename ...>
struct front : this_type<T1> {};

template <typename ... Ts>
using front_t = typename front<Ts...>::type;

template <typename ... Ts>
void Foo ()
{
    std::cout << std::is_same_v<front_t<Ts...>, int> << std::endl;
}

int main ()
{
    Foo<int, char, bool>();
}

However, I can't think of a generic way to access the back type without recursion. Intuitively I'd want to do something like this:

template <typename ...>
struct pack;

template <typename ...>
struct back;

template <typename Tn, typename ... Ts>
struct back <pack<Ts..., Tn>> : this_type<Tn> {};

template <typename ... Ts>
using back_t = typename back<pack<Ts...>>::type;

... but the variadic template needs to be the last argument in the specialisation.

We can use a bit of code bloat to manage accessing elements up to a certain amount. eg, 3:

template <typename ...>
struct back;

template <typename T1>
struct back <T1> : this_type<T1> {};

template <typename T1, typename T2>
struct back <T1, T2> : this_type<T2> {};

template <typename T1, typename T2, typename T3>
struct back <T1, T2, T3> : this_type<T3> {};

template <typename ... Ts>
using back_t = typename back<Ts...>::type;

template <typename ... Ts>
void Foo ()
{
    std::cout << std::is_same_v<back_t<Ts...>, bool> << std::endl;
}

int main ()
{
    Foo<int, char, bool>();
}

...But is there a way to do it generically without recursion?


Motivation:

Often we can change the way that we solve a problem so that we access from near the front of a variadic list, but sometimes it's unavoidable to access from the back. I don't want to create useless work for the compiler if there's some language feature that I'm not taking advantage of.

Elliott
  • 2,603
  • 2
  • 18
  • 35
  • 1
    This is a dupe as far as I can tell. If I've missed something, please let me know, and I'll vote to reopen. – cigien Oct 21 '20 at 22:01
  • 1
    @cigen, You’re right. Also there’s a promising answer to that question. Thanks! – Elliott Oct 21 '20 at 22:35

1 Answers1

-1

I'd leverage std::tuple to do most of the heavy lifting.

template<typename... Ts>
using back_t = std::tuple_element_t<std::tuple<Ts...>, sizeof...(Ts) - 1>;
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • Hmm. This is more-or-less what I was doing with my own version a tuple (doh!). However this doesn't quite answer the question. I just took a look at `cppref` and their "possible implementation" uses head-first recursion, so is probably slow to deduce types at the back. (https://en.cppreference.com/w/cpp/utility/tuple/tuple_element) – Elliott Oct 21 '20 at 08:21
  • @Elliott you can spelunk your vendor's `` to see what they actually do. It's called "possible implementation" not "mandatory implementation" for a reason – Caleth Oct 21 '20 at 08:26
  • Looking at GCC 10, it does it recursively too. This also doesn't ***quite*** convince me that it's not possible, because *if it is possible* to access from the back then `tuple_element_t` still doesn't have a strong incentive to use it because its users are just as likely to access from the front (you could use some conditional where if `index > size/2` then access from the back, but this might not be worth it from speed gain given the small additional overhead, especially in small variadics). – Elliott Oct 21 '20 at 08:53
  • So my question was marked as a duplicate - and some of the answers there show how to get `O(log(n))` recursion in `c++11`, and `O(1)` in `c++17`, but I think that I should point out that `std::tuple_element` is implemented with recursion in both `gcc` and `clang` (I haven't checked `msvc` yet) and so should really be avoided until it's fixed. It's extremely slow.. See `Julius`' answer: https://stackoverflow.com/a/47442393/8658157 – Elliott Oct 23 '20 at 04:54
  • Just FYI, I've since tested `std::tuple_element_t` on `msvc` and it was recursive also - and more importantly, much slower than the alternatives. – Elliott Oct 29 '20 at 06:46