2010-05-03 31 views
14

我正在写一个模板矢量型简单的数学库:具有共享功能的类模板特

template<typename T, size_t N> 
class Vector { 
    public: 
     Vector<T, N> &operator+=(Vector<T, N> const &other); 
     // ... more operators, functions ... 
}; 

现在我想专门为一些这样的一些额外功能。假设我想要x()y()上的Vector<T, 2>访问特定的坐标。我能为这个创造一个局部特殊化:

template<typename T> 
class Vector<T, 3> { 
    public: 
     Vector<T, 3> &operator+=(Vector<T, 3> const &other); 
     // ... and again all the operators and functions ... 
     T x() const; 
     T y() const; 
}; 

但现在我重复已经在通用模板中存在的一切。

我也可以使用继承。重命名通用模板VectorBase,我可以这样做:

template<typename T, size_t N> 
class Vector : public VectorBase<T, N> { 
}; 

template<typename T> 
class Vector<T, 3> : public VectorBase<T, 3> { 
    public: 
     T x() const; 
     T y() const; 
}; 

然而,现在的问题是,所有的运营商都在VectorBase定义的,所以它们返回VectorBase实例。这些不能被分配到Vector变量:

Vector<float, 3> v; 
Vector<float, 3> w; 
w = 5 * v; // error: no conversion from VectorBase<float, 3> to Vector<float, 3> 

我可以给Vector的隐式转换构造函数来实现这一目标:

template<typename T, size_t N> 
class Vector : public VectorBase<T, N> { 
    public: 
     Vector(VectorBase<T, N> const &other); 
}; 

不过,现在我把从VectorVectorBase,然后再返回。尽管内存中的类型是相同的,编译器可能会优化所有这些,但感觉笨重,我不太希望在本质上是编译时问题的情况下有潜在的运行时间开销。

有没有其他方法可以解决这个问题?

+0

为什么不只是制作'x()'和'y()'免费函数,采用适当的“Vector”特化?例如。 'template T x(const Vector & v);' – 2010-05-03 12:10:41

+0

可能的,但是'vx()'对我来说比'x(v)'更有意义另外,我想添加一些专门的构造函数,例如'Vector ( T,T)',并且构造函数不能是自由函数 – Thomas 2010-05-03 12:17:56

+0

不,但是你可以有函数按'std :: make_pair'的样式按值返回对象 – 2010-05-03 12:25:15

回答

8

我想你可以用CRTP来解决这个问题。这个习语用于boost::operator

template<typename ChildT, typename T, int N> 
class VectorBase 
{  
public: 
    /* use static_cast if necessary as we know that 'ChildT' is a 'VectorBase' */ 
    friend ChildT operator*(double lhs, ChildT const &rhs) { /* */ } 
    friend ChildT operator*(ChildT const &lhs, double rhs) { /* */ } 
}; 

template<typename T, size_t N> 
class Vector : public VectorBase<Vector<T,N>, T, N> 
{ 
}; 

template<typename T> 
class Vector<T, 3> : public VectorBase<Vector<T, 3>, T, 3> 
{ 
public: 
    T x() const {} 
    T y() const {} 
}; 

void test() 
{ 
    Vector<float, 3> v; 
    Vector<float, 3> w; 
    w = 5 * v; 
    w = v * 5; 
    v.x(); 

    Vector<float, 5> y; 
    Vector<float, 5> z; 
    y = 5 * z; 
    y = z * 5; 
    //z.x(); // Error !! 
} 
+0

我刚刚在其他方面工作一段时间后重新讨论了这个问题,这确实是最好的解决方案。太感谢了! – Thomas 2010-05-20 10:19:16

4

以下是我在使用C++ 0x功能回放时想出的东西。此处使用的唯一C++ 0x功能是static_assert,因此您可以使用Boost替换它。

基本上,我们可以使用一个静态大小检查函数,该函数只检查给定索引是否小于向量的大小。我们利用一个静态断言,以生成编译器错误,如果该指数超出范围:

template <std::size_t Index> 
void size_check_lt() const 
{ 
    static_assert(Index < N, "the index is not within the range of the vector"); 
} 

然后,我们可以提供一个get()方法返回一个给定的索引参考元素(显然是一个常量超载会太有用):

template <std::size_t Index> 
T& get() 
{ 
    size_check_lt<Index>(); return data_[Index]; 
} 

然后,我们可以写简单的访问器,像这样:

T& x() { return get<0>(); } 
T& y() { return get<1>(); } 
T& z() { return get<2>(); } 

如果向量只有两个元素,你可以使用X和Y,但不ž。如果矢量有三个或更多的元素,你可以使用全部三个元素。

我最终为构造函数做了同样的事情 - 我为二维,三维和四维矢量创建了构造函数,并添加了一个size_check_eq,它们仅允许为二维,三维和四维矢量实例化它们,分别。当我今晚回家时,如果有人感兴趣,我可以尝试发布完整的代码。

我放弃了一半的项目,所以这样做可能会有一些大问题,我没有遇到......至少这是一个可以考虑的选项。

0

最简单的方法?使用外部功能:

template <class T> 
T& x(Vector<T,2>& vector) { return vector.at<0>(); } 

template <class T> 
T const& x(Vector<T,2> const& vector) { return vector.at<0>(); } 

在模板编程使用外部功能是添加的功能,仅仅是因为你刚刚遇到的专业化问题的最简单的方法。

在另一方面,你仍然可以提供xyz任何N或者是使用enable_if/disable_if功能来限制范围。

0

我不知道你是否可以解决赋值运算符的打字问题,但是通过定义各种运算符的模板版本,实现它们的辅助函数,然后使用继承,可以使生活变得更容易一些。

template <typename T, std::size_t N> 
class fixed_array { 
public: 
    virtual ~fixed_array() {} 
    template <std::size_t K> 
    fixed_array& operator+=(fixed_array<T,K> const& other) { 
     for (std::size_t i=0; i<N; ++i) 
      this->contents[i] += other[i]; 
     return *this; 
    } 
    template <std::size_t K> 
    fixed_array& operator=(fixed_array<T,K> const& other) { 
     assign_from(other); 
     return *this; 
    } 
    T& operator[](std::size_t idx) { 
     if (idx >= N) 
      throw std::runtime_error("invalid index in fixed_array[]"); 
     return contents[idx]; 
    } 
protected: 
    template <std::size_t K> 
    void assign_from(fixed_array<T,K> const& other) { 
     for (std::size_t i=0; i<N; ++i) 
      this->contents[i] = other[i]; 
    } 
private: 
    T contents[N]; 
}; 

template <typename T> 
class fixed_2d_array: public fixed_array<T,2> { 
public: 
    T x_coord() const { return (*this)[0]; } 
    T y_coord() const { return (*this)[1]; } 
    template <std::size_t K> 
    fixed_2d_array& operator=(fixed_array<T,K> const& other) { 
     assign_from(other); 
     return *this; 
    } 
}; 

int 
main() { 
    fixed_array<int,5> ary1; 
    fixed_2d_array<int> ary2; 
    ary2 = ary1; 
    ary1 = ary2; 
    ary2 += ary1; 
    ary1 += ary2; 
    return 0; 
}