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.
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