Can't stop recursion, while iterating a template type list

74 Views Asked by At

So I have a template type list like this :

template <typename... Types>
struct type_list
{
};

I've made an accessor function like this :

template<class TypeList, size_t ElementIndex>
struct at;

template <template<typename...> class TypeList, typename Head, typename... OtherTypes, size_t ElementIndex>
struct at<  TypeList<Head, OtherTypes...>,  ElementIndex>
{
    static_assert(ElementIndex < (size_v<   TypeList<Head, OtherTypes...>   >), "at_t : ElementIndex is bigger than list size");

    using type = if_else_t < ElementIndex == 0, Head, typename at<  TypeList<OtherTypes...>, ElementIndex - 1   >::type >;
};

template <template<typename...> class TypeList, typename Last, size_t ElementIndex>
struct at<  TypeList<Last>, ElementIndex>
{
    static_assert(ElementIndex < (size_v<   TypeList<Last>  >), "at_t : ElementIndex is bigger than list size");

    using type = Last;
};

template<class TypeList, size_t ElementIndex>
using at_t = typename at<TypeList, ElementIndex>::type;

the if_else_t<> has the following implementation :

template<bool Condition, typename True, typename False>
struct if_else
{
    using type = True;
};

template<typename True, typename False>
struct if_else<false, True, False>
{
    using type = False;
};

Now if I test the function with :

static_assert(std::is_same_v<   bool, at_t< type_list<int, float, bool, char>, 2    >   >, "at_t : Bad result");

I trigger the static_assert that check if the ElementIndex is bigger than the list size. From the compiler's output I can clearly see that the at<> never stop the recursion, until ElementIndex hits his numerical limit (the case where ElementIndex = 0 - 1) and the static_assert is triggered.

What am I doing wrong ?

Ideal answer should also include a better, more elegant, implementation of at<> :)

Please note that I'm using MSVC and C++17.

2

There are 2 best solutions below

3
Artyer On BEST ANSWER

The problem is when you do this:

if_else_t < ElementIndex == 0, Head, typename at<  TypeList<OtherTypes...>, ElementIndex - 1   >::type >;

Even if ElementIndex is 0, the other two types still have to be evaluated to be passed to if_else_t (thus triggering the static_assert).

You fix it by using specialisation instead:

template<class TypeList, size_t ElementIndex>
struct at;

template <template<typename...> class TypeList, typename Head, typename... OtherTypes>
struct at<  TypeList<Head, OtherTypes...>,  0>
{
    using type = Head;
};

template <template<typename...> class TypeList, typename Head, typename... OtherTypes, size_t ElementIndex>
struct at<  TypeList<Head, OtherTypes...>,  ElementIndex>
{
    static_assert(ElementIndex < (size_v<   TypeList<Head, OtherTypes...>   >), "at_t : ElementIndex is bigger than list size");

    using type = typename at<  TypeList<OtherTypes...>, ElementIndex - 1   >::type;
};

template <template<typename...> class TypeList, size_t ElementIndex>
struct at<  TypeList<>,  ElementIndex>
{
    static_assert(ElementIndex != ElementIndex, "at_t : ElementIndex is bigger than list size");
};

Or you can use the standard tuple_element

template<class TypeList, size_t ElementIndex>
struct at;

template <template<typename...> class TypeList, typename... Types, size_t ElementIndex>
struct at<  TypeList<Types...>,  ElementIndex>
{
    static_assert(ElementIndex < (sizeof...(Types)), "at_t : ElementIndex is bigger than list size");

    using type = std::tuple_element_t<ElementIndex, std::tuple<Types...>>;
};

As a side note, if_else_t is just std::conditional_t

2
Jarod42 On

In

using type = if_else_t<ElementIndex == 0,
                       Head,
                       typename at<TypeList<OtherTypes...>, ElementIndex - 1>::type>;

typename at< TypeList<OtherTypes...>, ElementIndex - 1 >::type Has to be evaluated. and so instantiation of at<TypeList<OtherTypes...>, ElementIndex - 1>

You might delay(and so remove) instantiation with:

using type = typename if_else_t<ElementIndex == 0,
                                std::type_identity<Head>, // C++20, but trivial rewrite
                                at<TypeList<OtherTypes...>, ElementIndex - 1>>::type;

Demo