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

时间:2015-06-09 15:19:25

标签: c++ c++11 variadic-templates

我有一个可变的Engine模板类:

template <typename ... Components> class Engine;

我想在编译时为每个组件分配一个数字,这相当于它们的排序。拨打以下电话时会返回此信息:

template <typename Component> int ordinal();

例如,如果:

Engine<PositionComponent, PhysicsComponent, InputComponent> engine;
宣布了

,电话:

engine.ordinal<PhysicsComponent>();

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

是否有可能,如果是的话,怎么会这样呢?

4 个答案:

答案 0 :(得分:10)

您想在Component中找到Components...的索引吗?

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

正如构建的那样,尝试获取ordinal Component而不是Components...将是编译错误。这似乎是合适的。

答案 1 :(得分: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 types{using 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<?>有一个constexpr operator size_t()和一个operator(),因此您可以在大多数无缝地使用整数类型的位置使用它。

你也可以使用:

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

现在ordinal<Component>是一种表示组件索引的类型,而不是函数。

答案 2 :(得分:1)

我为了完整起见添加了这个,它利用了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...>();
}

答案 3 :(得分:0)

UNTESTED:

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...>();
}