如何在可变参数类模板中获取类型的索引?

Posted

技术标签:

【中文标题】如何在可变参数类模板中获取类型的索引?【英文标题】:How can I get the index of a type in a variadic class template? 【发布时间】:2015-06-09 15:19:25 【问题描述】:

我有一个可变参数引擎模板类:

template <typename ... Components> class Engine;

我想在编译时为每个组件分配一个编号,这相当于它们的顺序。这将在进行以下调用时返回:

template <typename Component> int ordinal();

例如,如果:

Engine<PositionComponent, PhysicsComponent, InputComponent> engine;

被声明,调用:

engine.ordinal<PhysicsComponent>();

将返回 1,而使用 InputComponent 而不是 PhysicsComponent 的类似调用将返回 2。

有没有可能,如果有,应该怎么做?

【问题讨论】:

我为元组找到了this solution,我很确定你可以根据你的用例调整它。 【参考方案1】:

所以你想在Components... 中找到Component 的索引?

template <typename... >
struct index;

// found it
template <typename T, typename... R>
struct index<T, T, R...>
: std::integral_constant<size_t, 0>
 ;

// still looking
template <typename T, typename F, typename... R>
struct index<T, F, R...>
: std::integral_constant<size_t, 1 + index<T,R...>::value>
 ;

用法:

template <typename Component> 
size_t ordinal()  return index<Component, Components...>::value; 

按照构造,尝试获取不在Components... 中的Componentordinal 将是一个编译错误。这似乎是合适的。

【讨论】:

非常聪明的解决方案,将index&lt;T,R...&gt;::value 的结果加到1! 谢谢。这很棒。你知道编译器是否会优化模板迭代吗? @NickCuthbert 它必须全部编译,但 index&lt;Component, Components...&gt;::value 是一个编译时常量 - 不涉及运行时工作。 @0x499602D2 谢谢。这很尴尬。【参考方案2】:

我下面的目标是尽可能地保持在编译时领域。

这是删除一些样板的别名。 std::integral_constant 是一个很棒的 std 类型,它存储编译时确定的整数类型:

template<std::size_t I>
using size=std::integral_constant<std::size_t, I>;

接下来,一个index_of类型,还有一个更容易使用的index_of_t

template<class...>struct typesusing type=types;;
template<class T, class Types>struct index_of;
template<class T, class...Ts>
struct index_of<T, types<T, Ts...>>:size<0>;
template<class T, class T0, class...Ts>
struct index_of<T, types<T0, Ts...>>:size<
  index_of<T,types<Ts...>>::value +1
>;

这个别名返回一个纯std::integral_constant,而不是从它继承的类型:

template<class T, class...Ts>
using index_of_t = size< index_of<T, types<Ts...>>::value >;

最后是我们的函数:

template <class Component>
static constexpr index_of_t<Component, Components...>
ordinal() const return;

它不仅是constexpr,它还返回一个将其值编码为其类型的值。 size&lt;?&gt; 有一个 constexpr operator size_t() 和一个 operator(),所以你可以在大多数需要无缝整数类型的地方使用它。

你也可以使用:

template<class Component>
using ordinal = index_of_t<Component, Components...>;

现在ordinal&lt;Component&gt; 是表示组件索引的类型,而不是函数。

【讨论】:

【参考方案3】:

为了完整起见,我添加了这个,它利用了 C++11 的 constexpr 功能和一些 stl 函数。我觉得它比其他解决方案更清洁。

//Same type
template <typename Target,typename T,typename ...Rest>
constexpr typename std::enable_if<std::is_same<Target,T>::value, size_t>
_ordinal()
    return 0;


//Different types
template <typename Target,typename T,typename ...Rest>
constexpr typename std::enable_if<!std::is_same<Target,T>::value, size_t>
_ordinal()
    return 1+_ordinal<Target,Rest...>();

【讨论】:

您缺少std::enable_if 中的::type 部分(即使您在那里确实有typename)。 我认为,当查找的类型不在列表中时,编译错误是相当不友好的(至少对于 GCC 6.3.0)。它并没有说太多关于真正的问题。虽然我不确定这里显示的其他解决方案在这种情况下是什么样子。【参考方案4】:

未经测试:

template <int, typename>
constexpr int index_of()  return -1;  // type not found

template <int N, typename Component, typename Cur, typename... Components>
constexpr int index_of() 
    return std::is_same<Component, Cur>::value ? N : index_of<N+1, Component, Components...>();


template <typename... Components>
template <typename Component>
constexpr int engine<Components...>::ordinal() 
    return index_of<0, Component, Components...>();

可以使用结构,但我发现这样更干净(没有所有::type 丑陋)。

如果您想在找不到类型时出现编译时错误,请将ordinal 更改为:

template <typename... Components>
template <typename Component>
constexpr int engine<Components...>::ordinal() 
    static_assert(index_of<0, Component, Components...>()!=-1, "invalid component");
     return index_of<0, Component, Components...>();

【讨论】:

以上是关于如何在可变参数类模板中获取类型的索引?的主要内容,如果未能解决你的问题,请参考以下文章

如何按索引从可变参数模板参数包中提取值?

如何使用可变参数模板参数保存可变数量的参数?

每个可变参数模板参数生成一个类成员

从结构中获取所有可变参数模板类型,该结构是函数模板中的类型参数

在可变参数模板类中初始化静态数组

可变参数模板