模板化矢量和颜色数学库(专业化)

时间:2010-02-13 15:47:27

标签: c++

我创建了一个通过模板操作的数学库,它允许用户在类中指定数组的大小和类型,然后用于创建任何维度最多为4的数学向量。一旦我去创建一个颜色类,它就让我感觉到矢量和颜色类有多相似。无论如何,我可以减少代码重用并使用某种形式的继承或专门化来分离:

  1. 特定功能(即vector3没有setXYZW()函数,而只有setXYZ()),它只能用于它的维度。
  2. 颜色类和向量类都可以(就数组数据成员而言)的大小范围从1到4并且两者共享相同的运算符,但在某些情况下它们的使用不同,例如乘法向量不同于乘以颜色。
  3. 我对模板的了解并不是那么好,所以如果有人能告诉我这种情况的最佳解决方案,我将非常感激吗?

    template < std::size_t N = 3, typename T = float >
    class Vector
    {
      typedef T Degree, Radian;
    private:
      T m_vecEntry[N];
    public:
      // arithmetic operations
      Vector operator + (const Vector & _vector) const;
      Vector operator - (const Vector & _vector) const;
      Vector operator * (const Vector & _vector) const;
      Vector operator * (float _val) const;
    };
    

    template < std::size_t N = 4, typename T = float >
    class Colour
    {
    private:
      T m_colEntry[N];
    public:
      // arithmetic operations
      Colour operator + (const Colour& _colour) const;
      Colour operator - (const Colour& _colour) const;
      Colour operator * (const Colour& _colour) const;
      Colour operator * (float _val) const;
    };
    

3 个答案:

答案 0 :(得分:2)

您的课程有相当多的重复代码,建议您对此做些什么。可能的解决方案如下。

首先,您将常用功能应用于基类:

template <class Derived, std::size_t N, typename T>
class VectorBase
{
protected:
  VectorBase() {} // Prevent instantiation of base

  Derived operator + (const Derived & _vector) const {
      std::cout << "Base addition\n";
      return Derived();
  }
  Derived operator * (T _val) const {
      std::cout << "Base scalar multiplication\n";
      return Derived();
  }

  T m_components[N];
};

然后您从中派生出VectorColour类。在每个派生类中,使用using Base::operation;明确声明来自基类的相应操作在派生类中是有意义的。

对于在派生类中没有意义的操作,您提供了替代定义或根本不提供它(由于您没有编写using,因此无法访问它)。

您还可以添加不在基类中的操作,例如Vector::norm

template < std::size_t N = 3, typename T = float >
class Vector : VectorBase<Vector<N, T>, N, T>
{
  typedef VectorBase<Vector<N, T>, N, T> Base;
  typedef T Degree, Radian;

public:
  using Base::operator+; // Default implementation is valid
  using Base::operator*; // Default implementation is valid
  T norm() const {   // Not present in base class
      return T();
  }  
};

template < std::size_t N = 4, typename T = float >
class Colour : VectorBase<Colour<N, T>, N, T>
{
  typedef VectorBase<Colour<N, T>, N, T> Base; 
public:
  using Base::operator+; // Default implementation is valid

  Colour operator * (T _val) const {  // Redefines version in base class
      std::cout << "Colour scalar multiplication\n";
      return Colour();
  }
};

此代码中唯一的技巧是我使用CRTP使基类操作与派生类型一起使用。

这是一个小测试程序:

int main()
{
  Vector<> va, vb;
  va + vb;
  va.norm();
  va * 3.0;

  Colour<> ca, cb;
  ca + cb;
  ca * 3.0f;
}

打印:

Base addition
Base scalar multiplication
Base addition
Colour scalar multiplication

答案 1 :(得分:1)

最终,编译器只会创建它决定要使用的模板特化部分。因此,它会影响您的优化,例如没有创建未使用的方法等。

所以你可能只想考虑制作普通的预处理器宏来包装每个模板的轻微调整。

但很明显,如果你想做一些专业化,你也可以做到这一点,但你仍然会最终复制代码行;)

答案 2 :(得分:1)

许多图形引擎将两者分开,正是因为你提到的原因。虽然数据结构相同,但对数据操作都需要不同的语义。这也有更有意义的函数名称(setX vs setRed)的额外好处,但意味着代码重复。

您的实现只是固定大小数组的包装。您可以在(可能的抽象)基类中移动数据和共享功能,并在子类中提供特定功能。

另一种方法是将矢量和颜色类视为数组包装器的装饰器。在数组周围编写或使用包装器,并通过合成将其与矢量/颜色功能相结合。