迭代模板类型列表时无法停止递归

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

所以我有一个这样的模板类型列表:

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<> 的更好、更优雅的实现 :)

请注意,我使用的是 MSVC 和 C++17。

问题是当你这样做的时候:

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

即使ElementIndex0,其他两种类型仍然需要被评估才能传递给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

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