如何使用C++模板参数来确定类中的成员类型

How can I use C++ template argument to decide which type of member is in a class

本文关键字:成员 类型 成员类 C++ 何使用 参数      更新时间:2023-10-16

我想创建一个顶点类,并希望通过能够创建 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;
    }
};

我不认为语句在编译时没有被计算,所以这只是我想做什么的一个说明。有什么办法可以做到这一点吗?还是我必须专门化每种类型,或者只是重写所有不同的版本?

您可能希望拥有某种vec3vec2选择器类型。如果已经有 vec3vec2 的模板化版本,只需使用它们。否则,您可以使用模板专用化:

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上重复任何成员函数(否则会使代码更复杂)

这是一个替代方案

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模板上多做一点努力,你可以对lambda的代码进行相当多的改进(也许你已经看到了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;

OP在评论中提到他们正在使用GML。

GLM 向量实际上是模板,因此不需要复杂的解决方案:

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

这只是贾斯汀解决方案的补充。反正其实是他的。使用std::condition的想法是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 的评论中给出了两种不同的解决方案。