C++ 我是否在这里滥用了模板?

C++ 我是否在这里滥用了模板?,c++,templates,C++,Templates,我试图为一个网格类编写通用程序。(我需要CPU变换来变形网格,这就是为什么不在GPU中执行此操作的原因,如果您想问的话) 网格包含3d形状的顶点以及面连接(顶点连接在一起形成三角形) 这里T是顶点的类型,一些类型的模型有PNCT类型(位置、法线、颜色、texcoord),而其他类型的模型只是PC(位置、颜色) 当然,所有顶点都有一个位置 但这是我的问题 Mesh必须实现变换方法。当然,每个顶点格式都有一个位置,如果名称一致(.pos),则该方法可以正常工作: 由于每个顶点肯定都有一个位置,如果我

我试图为一个网格类编写通用程序。(我需要CPU变换来变形网格,这就是为什么不在GPU中执行此操作的原因,如果您想问的话)

网格
包含3d形状的顶点以及面连接(顶点连接在一起形成三角形)

这里
T
是顶点的类型,一些类型的模型有PNCT类型(位置、法线、颜色、texcoord),而其他类型的模型只是PC(位置、颜色)

当然,所有顶点都有一个位置

但这是我的问题

Mesh
必须实现
变换
方法。当然,每个顶点格式都有一个位置,如果名称一致(
.pos
),则该方法可以正常工作:

由于每个顶点肯定都有一个位置,如果我总是在
顶点*
结构中调用该成员
.pos
,那么我可以向模板类
网格
添加一个方法
.transform
,如下所示:

void transform( Matrix4& mat )
{
    each vertex
      vertex.pos = mat * vertex.pos ; // transform it
}
现在问题来了。如果顶点格式中有法线,则该法线也必须变换(通过“法线矩阵”)。现在我的模板中有一个“if语句”?我必须为每个顶点类型编写一个模板专门化
.transform
方法(基本上分为两类,有法线的和没有法线的)

我是否在这里滥用了模板?错过了一些“编程船”

以下是我在逻辑上真正想要做的:

void transform( Matrix4& mat )
{
    each vertex in vertices
    {
      vertex.pos = mat * vertex.pos ; // transform it
      #ifdef vertex.normal
      vertex.normal = mat * vertex.normal ; // transform normal as well*
      #endif
    }
}
*在转换过程中假定没有规模
(那么您就不需要使用“法线矩阵”)

您可能想要创建结构类,并从两个基类派生它们,一个带法线,一个不带法线。然后您可以使用模板专门化在这两个基类(而不是所有顶点类)之间进行选择。

如果您可以编写一个
顶点
的层次结构,其中基类只保留位置(和
.pos
成员),那么
VertexWNormal
将保留
.normal
,其余的继承自此,然后,您可以只添加非模板重载,并让编译器处理:

void transform( Matrix4& m, VertexBase& v ) {
   // transform only pos
}
void transform( Matrix4& m, VertexWNormal& v ) {
   transform(m,static_cast<VertexBase&>(v));
   // transform normal here
}
void tranform( Matrix4& m ) {
   foreach vertex:
       transform(m,vertex);
}
void变换(Matrix4&m、VertexBase&v){
//仅转换pos
}
无效变换(矩阵4&m、顶点W法线&v){
变换(m,静态_-cast(v));
//在这里变换法线
}
void Transform(Matrix4&m){
foreach顶点:
变换(m,顶点);
}

当然,这对你的设计是否有意义取决于你没有展示的很多东西,这很难说。

如果你只有两种类型的顶点,我建议你做David上面描述的:只需创建两个函数进行转换,并根据顶点类型使用重载调用它们。这也适用于更多的顶点类型,但每次添加新的顶点类型时,都需要再次重载该函数。对于这里描述的简单函数,这可能没问题,但如果函数实际上更复杂,它可能会变得烦人

部分修复是创建一个traits类,该类告诉特定顶点类型是否具有普通成员。可以设置默认值,使其在大多数情况下都是正确的,并且可以根据特征选择合适的函数。您仍然可以提供两个版本的代码,但对于每一个额外的顶点类型,需要定义的只是特征:

template <typename> struct VertexHasNormal { enum { value = false }; };
template <> struct VertexHasNormal<VertexPNCT> { enum { value = true }; };

template <typename V, template T, template S>
void transform(V& vertices, Matrix4& m, T S::*member) {
    for (auto& v: vertices) {
        v.*member = mat * v.*member;
    }
}

template <bool, typename T>
struct Transform {
    template <typename V>
    void call(V& vertices, Matrix4& m) {
        transform(vertices, m, &T::pos);
    }
};
template <typename T>
struct Transform<bool, T> {
    template <typename V>
    void call(V& vertices, Matrix4& m) {
        transform(vertices, m, &T::pos);
        transform(vertices, m, &T::normal);
    }
};

template <typename T>
void Mash<T>::transform(Matrix4& m) {
    Transform<VertexHasNormal::value>::call(this->vertices, m);
}
模板结构{enum{value=false};};
模板结构VertExpressMan{enum{value=true};};
模板
无效变换(V和顶点、矩阵4和m、T和S::*成员){
用于(自动&v:顶点){
v、 *成员=mat*v.*成员;
}
}
模板
结构变换{
模板
无效调用(V&V顶点、Matrix4&m){
变换(顶点、m和T::pos);
}
};
模板
结构变换{
模板
无效调用(V&V顶点、Matrix4&m){
变换(顶点、m和T::pos);
变换(顶点、m和T::法线);
}
};
模板
voidmash::transform(Matrix4&m){
Transform::call(此->顶点,m);
}
函数模板
transform()
是对一系列顶点执行实际变换的函数。我考虑了它,因为我认为它有点可爱,可以考虑,但不需要考虑。它也不需要使用指向成员的指针,
auto
等。
Transform
类型只是使用的辅助类型,因为函数模板不能部分专用。它专门研究顶点类型是否具有
normal
成员的特征。最后,
Mash::transform()

唯一需要做的是在定义另一个具有
normal
成员的顶点时添加一个新的特征专门化。这可能不可取。在这种情况下,可以使用类型特征确定结构是否具有名为
normal
的可访问数据成员。然而,我不认为我可以从我的头顶打字。实现它的基本思想是利用替换失败不是错误这一事实(“
如果被测试的类型确实有必要的成员,那么可以设置一些东西来消除两个潜在成员之间的歧义。有一个Boost组件可以实现这一点,但是如果您需要自己创建它,大约需要10到10行代码。

Matrix4的类型是什么?哪个类拥有
转换
函数(还是自由函数)?
顶点的类型是什么?所有这些问题以及可能更多的问题都会影响解决方案。你应该展示更多的实际问题。我想如果我理解正确,transform在
Mesh
类中。是否有必要在运行时选择正确的方法。或者可以静态绑定它?你可能
template <typename> struct VertexHasNormal { enum { value = false }; };
template <> struct VertexHasNormal<VertexPNCT> { enum { value = true }; };

template <typename V, template T, template S>
void transform(V& vertices, Matrix4& m, T S::*member) {
    for (auto& v: vertices) {
        v.*member = mat * v.*member;
    }
}

template <bool, typename T>
struct Transform {
    template <typename V>
    void call(V& vertices, Matrix4& m) {
        transform(vertices, m, &T::pos);
    }
};
template <typename T>
struct Transform<bool, T> {
    template <typename V>
    void call(V& vertices, Matrix4& m) {
        transform(vertices, m, &T::pos);
        transform(vertices, m, &T::normal);
    }
};

template <typename T>
void Mash<T>::transform(Matrix4& m) {
    Transform<VertexHasNormal::value>::call(this->vertices, m);
}