Не удается остановить рекурсию при повторении списка типов шаблона - PullRequest
0 голосов
/ 27 мая 2020

Итак, у меня есть список типов шаблонов вроде этого:

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

Я создал такую ​​функцию доступа:

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;

if_else_t<> имеет следующую реализацию:

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;
};

Теперь, если я тестирую функцию с помощью:

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

, я запускаю static_assert, который проверяет, больше ли ElementIndex, чем размер списка. Из вывода компилятора я ясно вижу, что at<> никогда не останавливает рекурсию, пока ElementIndex не достигнет своего числового предела (случай, когда ElementIndex = 0 - 1) и сработает static_assert.

Что я делаю неправильно?

Идеальный ответ также должен включать лучшую, более элегантную реализацию at<>:)

Обратите внимание, что я использую MSV C и C ++ 17.

Ответы [ 2 ]

2 голосов
/ 27 мая 2020

In

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

typename at< TypeList<OtherTypes...>, ElementIndex - 1 >::type Требуется оценка. и поэтому создание экземпляра at<TypeList<OtherTypes...>, ElementIndex - 1>

Вы можете отложить (и таким образом удалить) создание экземпляра с помощью:

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

Demo

2 голосов
/ 27 мая 2020

Проблема в том, когда вы делаете это:

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

Даже если ElementIndex равно 0, два других типа все равно должны быть оценены для передачи в if_else_t (таким образом, вызывая static_assert).

Вы можете исправить это, используя вместо этого специализацию:

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");
};

Или вы можете использовать стандартный 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...>>;
};

В качестве примечания, if_else_t просто std::conditional_t

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...