2010-05-03 58 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; 
}