如何区分模板类中存储的类型

Posted

技术标签:

【中文标题】如何区分模板类中存储的类型【英文标题】:How to distinguish the type stored in a templated class 【发布时间】:2016-03-25 19:19:28 【问题描述】:

假设我有一个类伪装成其模板包中的类型之一,我如何获取函数中一个模板参数所在的索引或当前索引指向的类型?

template<typename... Vs>
struct TypeIndex 

    int index_of_type;

    void some_function() 
        cout << static_cast<get_type_from_index()> (some_member);
    
;

我知道这里需要进行一些类专业化,但我不能完全理解如何在代码中实现它。任何帮助都会很棒,谢谢!

【问题讨论】:

我不确定这是否完全可能 - 因为索引将在运行时解析,但类型需要在编译时解析。部分解决方案是,如果所有模板参数类型都有一个公共基类,并且只需要调用者 dynamic_cast @DrewMcGowen 嗯,这就是我的想法。那么 boost::variant 是如何通过 apply_visitor 函数做到这一点的呢? 我对boost不熟悉,所以我得去找源代码看看它是怎么做的。 @Curious 我相信他们使用类型擦除,有点像std::function 所做的。 T和“current_type_from_index()”是什么关系? 【参考方案1】:

获取一个模板参数在函数中的索引

有两百万种方法可以做到这一点。这是一个:

// base case is "not found"
template<size_t N, class T, class... Ts>
struct index_of_impl : std::integral_constant<std::size_t, std::size_t(-1)> ;

template<size_t N, class T, class... Ts>
struct index_of_impl<N, T, T, Ts...> : std::integral_constant<std::size_t, N> ;

template<size_t N, class T, class U, class... Ts>
struct index_of_impl<N, T, U, Ts...> : index_of_impl<N+1, T, Ts...> ;

template<class T, class... Ts>
struct index_of : index_of_impl<0, T, Ts...> ;

当前索引指向哪个类型

索引的值只有在运行时才知道,而 C++ 是静态类型的,所以这是不可能的。

对于编译时索引,这是微不足道的:

template<size_t I, class... Ts>
using at = std::tuple_element_t<I, std::tuple<Ts...>>;

但是boost::variant 如何使用apply_visitor 做到这一点 功能?

apply_visitor 的想法是将其编译为类似switch 的构造:

// pseudocode
switch(index) 
    case 0:
        return visitor(get<0>(variant));
        break;
    case 1:
        return visitor(get<1>(variant));
        break;
    // etc.
;

请注意,visitor 需要与变体中的 每个 类型一起使用,并且在每种情况下都返回相同的类型,这就是上述工作的原因。

当然,实际上,如果可能的类型数量是无限的(在 C++03 中 - 没有可变参数模板 - boost::variant 显然对它的类型数量有上限可以处理,所以如果他们愿意,他们可以直接写一个switch)。最简单的方法是简单地尝试每个索引,直到找到正确的索引。草图:

template<size_t N>
struct impl 
    template<class Visitor, class Variant>
    decltype(auto) apply(Visitor visitor, const Variant& variant)
        if(variant.index == N) return visitor(get<N>(variant));
        return impl<N-1>::apply(visitor, variant);
    
;

template<>
struct impl<0>
    template<class Visitor, class Variant>
    decltype(auto) apply(Visitor visitor, const Variant& variant)
        assert(variant.index == 0);
        return visitor(get<0>(variant));
    
;

template<class Visitor, class Variant>
decltype(auto) apply_visitor(Visitor visitor, const Variant& variant)
    return impl<variant_size<Variant>::value>::apply(visitor, variant);

有更有效的方法(例如,进行二分查找或使用跳转表)。

【讨论】:

感谢您的回答。最后一件事。 get&lt;x&gt;如何知道根据x的类型需要返回哪个类型? 1) 阅读答案。 2) 没有。 这是一个很好的答案!抱歉之前的愚蠢问题。我真的很累,无法思考。我永远不会想到我自己的这种带有模板的递归。非常感谢!【参考方案2】:

下面的课程正在处理您其他问题的答案中的代码。我没有包含它,因为依赖它的析构函数调度还没有实现。

像这样使用它:using my_type = pack_type_at&lt;index, Vs...&gt;::type;,其中index 是编译时可用的整数,就像来自模板参数一样。

投票前请注意:这仅适用于编译时索引,因此不能完全回答问题。在其他地方我说这个任务是“不平凡的”,因为需要使用运行时索引以某种方式找到并运行与包索引之一相对应的适当函数重载。

template <int IDX, int CURI, bool ATIDX, typename V, typename ...Vs>
struct pack_type_at_helper 
    typedef typename pack_type_at_helper<IDX, CURI+1, CURI+1==IDX, Vs...>::type type;
;

template <int IDX, int CURI, typename V, typename ...Vs>
struct pack_type_at_helper<IDX, CURI, true, V, Vs...> 
    typedef V type;
;

template <int IDX, typename ...Vs>
struct pack_type_at 
    typedef typename pack_type_at_helper<IDX, 0, IDX==0, Vs...>::type type;
;

【讨论】:

以上是关于如何区分模板类中存储的类型的主要内容,如果未能解决你的问题,请参考以下文章

如何将模板类添加为普通类中的成员

第58课 类模板的概念和意义

在 C++ 中的多个类中使用模板类

将类的任何模板存储在另一个类中

BST模板类中使用的矢量模板类

部分模板类中不允许指向不完整类类型的指针