0

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

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

我触发了检查 ElementIndex 是否大于列表大小的 static_assert。从编译器的输出中,我可以清楚地看到at<>永远不会停止递归,直到 ElementIndex 达到他的数值限制(ElementIndex = 0 - 1 的情况)并且触发了 static_assert。

我究竟做错了什么 ?

理想的答案还应该包括更好、更优雅的实现at<>:)

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

4

2 回答 2

2

问题是当你这样做时:

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

即使ElementIndexis 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

于 2020-05-27T17:11:30.177 回答
2

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;

演示

于 2020-05-27T17:14:33.943 回答