Как я могу использовать аргумент шаблона С++, чтобы решить, какой тип члена принадлежит классу

Я хотел бы создать класс Vertex и хотел бы обобщить его, имея возможность создать 32-битную версию с плавающей и 64-разрядной версиями и, возможно, версию int. Я хотел бы сделать это:

template <typename P>
struct Vertex
{
    if (typeid(P) == typeid(float))
    {
         vec3 position;
         vec3 normal;
         vec2 texcoords;
    }
    else if (typeid(P) == typeid(double))
    {
         dvec3 position; // This is a double vector
         dvec3 normal;
         dvec2 texcoords;
    }
    else if (typeid(P) == typeid(int))
    {
         ivec3 position; // This is an integer vector
         ivec3 normal;
         ivec2 texcoords;
    }

};

Я не думаю, что если инструкции не оцениваются во время компиляции, это просто иллюстрация того, что я хотел бы сделать. Есть какой-либо способ сделать это? Или я должен специализироваться на каждом типе или просто переписывать все разные версии?

Ответ 1

Возможно, вам понадобится тип селектора vec3 и vec2. Если уже есть шаблонные версии vec3 и vec2, просто используйте их. В противном случае вы можете использовать специализированную специализацию:

template <typename T>
struct vec_selector {};

template <>
struct vec_selector<float> {
    using vec3_type = vec3;
    using vec2_type = vec2;
};

template <>
struct vec_selector<double> {
    using vec3_type = dvec3;
    using vec2_type = dvec2;
};

template <>
struct vec_selector<int> {
    using vec3_type = ivec3;
    using vec2_type = ivec2;
};

template <typename P>
using vec3_select_t = typename vec_selector<P>::vec3_type;

template <typename P>
using vec2_select_t = typename vec_selector<P>::vec2_type;

Тогда вы можете просто написать:

template <typename P>
struct Vertex
{
    vec3_select_t<P> position;
    vec3_select_t<P> normal;
    vec2_select_t<P> texcoords;
};

Вы также можете просто специализировать шаблон Vertex, но, похоже, было бы полезно иметь vec3_select_t в другом месте, и вам придется повторять любые функции-члены на Vertex (или сделать код сложнее)

Ответ 2

Здесь альтернатива

template<typename T>
struct Type { typedef T type; };

template<typename T>
inline constexpr Type<T> type{};

template <typename P>
struct Vertex
{
    static constexpr auto D3 = []{ 
        if constexpr(std::is_same_v<P,float>)
            return type<vec3>;
        else if constexpr(std::is_same_v<P,double>)
            return type<dvec3>;
        else if constexpr(std::is_same_v<P,int>)
            return type<ivec3>;
    }();

    static constexpr auto D2 = []{ 
        if constexpr(std::is_same_v<P,float>)
            return type<vec2>;
        else if constexpr(std::is_same_v<P,double>)
            return type<dvec2>;
        else if constexpr(std::is_same_v<P,int>)
            return type<ivec2>;
    }();

    typename decltype(D3)::type position;
    typename decltype(D3)::type normal;
    typename decltype(D2)::type texcoords;
};

С небольшим усилием в шаблоне Type вы можете немного улучшить код лямбда (возможно, вы видели boost hana, который следует за этой идеей)

template<typename T>
struct Type {
   typedef T type;   
   friend constexpr bool operator==(Type, Type) {
       return true;
   }
};

template<typename T1, typename T2>
constexpr bool operator==(Type<T1>, Type<T2>) {
    return false;
}

template<typename T>
inline constexpr Type<T> type{};

Теперь ему больше не понадобится std::is_same_v

template <typename P>
struct Vertex
{
    static constexpr auto D3 = [](auto t) { 
        if constexpr(t == type<float>)
            return type<vec3>;
        else if constexpr(t == type<double>)
            return type<dvec3>;
        else if constexpr(t == type<int>)
            return type<ivec3>;
    }(type<P>);

    static constexpr auto D2 = [](auto t) { 
        if constexpr(t == type<float>)
            return type<vec2>;
        else if constexpr(t == type<double>)
            return type<dvec2>;
        else if constexpr(t == type<int>)
            return type<ivec2>;
    }(type<P>);

    typename decltype(D3)::type position;
    typename decltype(D3)::type normal;
    typename decltype(D2)::type texcoords;
};

Уродливая запись decltype может быть исправлена ​​с помощью auto а также

template<auto &t>
using type_of = typename std::remove_reference_t<decltype(t)>::type;

Итак, вы можете написать

type_of<D3> position;
type_of<D3> normal;
type_of<D2> texcoords;

Ответ 3

OP упомянул в комментариях, что они используют GML.

векторы GLM на самом деле являются шаблонами, поэтому нет необходимости в сложных решениях:

template <typename P>
struct Vertex
{
     tvec3<P> position;
     tvec3<P> normal;
     tvec2<P> texcoords;
};

Ответ 4

Это просто дополнение к решению Justin. Это на самом деле его во всяком случае. Идея использовать std:: условный была идея François Andrieux, приведенная в комментариях. Что-то в этом роде:

template <typename P>
struct vertex
{
    using vec3_t = std::conditional_t <std::is_same_v<float, P>,
        /*IF FLOAT*/     vec3,
        /*OTHERWISE*/    std::conditional_t <is_same_v<double, P>,
        /*IF DOUBLE*/    dvec3,
        /*IF INT*/       ivec3>>;

    vec3_t position;
    vec3_t normal;
    //vec2_t texcoords; WILL HAVE TO TYPEDEF THIS AS WELL.
};

Johannes Schaub дал два разных решения в комментариях на основе constexpr и decltype.