How can I get the index of a type in a variadic class template?

前端 未结 4 479
盖世英雄少女心
盖世英雄少女心 2021-01-03 02:11

I have a variadic Engine template class:

template  class Engine;

I\'d like to assign a number to each compon

相关标签:
4条回答
  • 2021-01-03 02:55

    So you want to find the index of Component in 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>
    { };
    

    Usage:

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

    As constructed, trying to get the ordinal of a Component not in Components... will be a compile error. Which seems appropriate.

    0 讨论(0)
  • 2021-01-03 03:02

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

    I could have used structs, but I find this much cleaner (without all the ::type ugliness).

    If you want a compile-time error when the type is not found, change ordinal to:

    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...>();
    }
    
    0 讨论(0)
  • 2021-01-03 03:03

    My goal below is to keep things in the compile-time realm as much as possible.

    This is an alias to remove some boilerplate. std::integral_constant is a wonderful std type that stores a compile-time determined integer-type:

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

    Next, a index_of type, and an index_of_t that is slightly easier to use:

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

    This alias returns a pure std::integral_constant, instead of a type inheriting from it:

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

    Finally our function:

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

    it is not only constexpr, it returns a value that encodes its value in its type. size<?> has a constexpr operator size_t() as well as an operator(), so you can use it in most spots that expect integer types seamlessly.

    You could also use:

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

    and now ordinal<Component> is a type representing the index of the component, instead of a function.

    0 讨论(0)
  • 2021-01-03 03:06

    I'm adding this for completeness sake, it utilizes C++11's constexpr functionality, and a few stl functions. I feel it is a little cleaner than the other solutions.

    //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...>();
    }
    
    0 讨论(0)
提交回复
热议问题