2012-08-09 84 views
0

我想實現一個簡單的多維點類與模板(學習)。我需要兩個專業Point2D和Point3D - 這是我迄今爲止允許構造函數直接初始化Point Point(像點1,2)一樣。雖然這段代碼編譯並正常工作,但我不喜歡的是專業化中的代碼重複部分 - 我一定在做錯事。C++模板 - 簡單點類

我是新來的C + + /模板 - 任何幫助表示讚賞。

#ifndef POINT_H_ 
#define POINT_H_ 

template< typename T, int Dimensions = 2 > 
class Point 
{ 
public: 
    typedef typename T value_type; 

    Point() { std::fill(elements_, elements_+Dimensions, 0); } 
    Point(const Point<T, Dimensions>& rhs) : elements_(rhs.elements_) {} 
    ~Point() {} 


    Point & operator=(const Point<T, Dimensions>& rhs) { return *this; } 

    const Point operator+(const Point<T, Dimensions>& p) 
    { 
     Point<T, Dimensions> ret; 

     for(int i = 0; i < Dimensions; i++) 
     { 
      ret[i] += elements_[i] + p[i]; 
     } 

     return ret; 
    } 

    Point & operator+=(const Point<T, Dimensions>& p) 
    { 
     for(int i = 0; i < Dimensions; i++) 
     { 
      elements_[i] += p[i]; 
     } 

     return *this; 
    } 

    Point & operator-=(const Point<T, Dimensions> & p) 
    { 
     for(int i = 0; i < Dimensions; i++) 
     { 
      elements_[i] -= p[i]; 
     } 

     return *this; 
    } 

    T & operator[](const size_t index) 
    { 
     return elements_[index]; 
    } 

private: 
    T elements_[Dimensions]; 
}; 

template<typename T> 
class Point< T, 2 > 
{ 
public: 
    Point(const T x, const T y) 
    { 
     elements_[0] = x; 
     elements_[1] = y; 
    } 

    typedef typename T value_type; 

    Point() { std::fill(elements_, elements_+Dimensions, 0); } 
    Point(const Point<T, 2>& rhs) : elements_(rhs.elements_) {} 
    ~Point() {} 


    Point & operator=(const Point<T, 2>& rhs) { return *this; } 

    const Point operator+(const Point<T, 2>& p) 
    { 
     Point<T, 2> ret; 

     for(int i = 0; i < 2; i++) 
     { 
      ret[i] += elements_[i] + p[i]; 
     } 

     return ret; 
    } 

    Point & operator+=(const Point<T, 2>& p) 
    { 
     for(int i = 0; i < 2; i++) 
     { 
      elements_[i] += p[i]; 
     } 

     return *this; 
    } 

    Point & operator-=(const Point<T, 2> & p) 
    { 
     for(int i = 0; i < 2; i++) 
     { 
      elements_[i] -= p[i]; 
     } 

     return *this; 
    } 

    T & operator[](const size_t index) 
    { 
     return elements_[index]; 
    } 

private: 
    T elements_[2]; 
}; 


template< typename T> 
class Point< T, 3 > 
{ 
public: 

    Point(const T x, const T y, const T z) 
    { 
     elements_[0] = x; 
     elements_[1] = y; 
     elements_[2] = z; 
    } 

    typedef typename T value_type; 

    Point() { std::fill(elements_, elements_+3, 0); } 
    Point(const Point<T, 3>& rhs) : elements_(rhs.elements_) {} 
    ~Point() {} 


    Point & operator=(const Point<T, 3>& rhs) { return *this; } 

    const Point operator+(const Point<T, 3>& p) 
    { 
     Point<T, 3> ret; 

     for(int i = 0; i < 3; i++) 
     { 
      ret[i] += elements_[i] + p[i]; 
     } 

     return ret; 
    } 

    Point & operator+=(const Point<T, 3>& p) 
    { 
     for(int i = 0; i < 3; i++) 
     { 
      elements_[i] += p[i]; 
     } 

     return *this; 
    } 

    Point & operator-=(const Point<T, 3> & p) 
    { 
     for(int i = 0; i < 3; i++) 
     { 
      elements_[i] -= p[i]; 
     } 

     return *this; 
    } 

    T & operator[](const size_t index) 
    { 
     return elements_[index]; 
    } 

private: 
    T elements_[3]; 
}; 

typedef Point< int, 2 > Point2Di; 
typedef Point< int, 3 > Point3Di; 


#endif //POINT_H_ 
+0

我不明白,你需要在所有專注這個類的任何原因。它應該都沒有專業化(除了可能的一些小調整)。 – Chad 2012-08-09 20:34:19

+1

在我看來,您可以將大量重複代碼放入您的主類和它的專業化派生自的通用基類中。 – jahhaj 2012-08-09 20:36:49

+0

@Chad,我想要專注於此的唯一原因是爲了方便添加構造函數Point (T x,T y)。 – user1588625 2012-08-09 20:42:59

回答

5

你可以–只需–在主模板中提供了2D和3D構造函數。

沒有必要與基類和其他在這裏向Rube Goldberg solutions調戲,因爲沒有要解決的問題:我們在模板的土地,其中未使用的東西是根本不用。

例子:

#ifndef POINT_H_ 
#define POINT_H_ 

#include <array>   // std::array 

#define STATIC_ASSERT(e) static_assert(e, "!(" #e ")") 

template< typename T, int nDimensions = 2 > 
class Point 
{ 
private: 
    std::array< T, nDimensions > elements_; 

public: 
    typedef T ValueType; 

    T& operator[](int const i) 
    { 
     return elements_[i]; 
    } 

    T const& operator[](int const i) const 
    { 
     return elements_[i]; 
    } 

    void operator+=(Point const& other) 
    { 
     for(int i = 0; i < nDimensions; ++i) 
     { 
      elements_[i] += other.elements_[i]; 
     } 
    } 

    void operator-=(Point const& other) 
    { 
     for(int i = 0; i < nDimensions; ++i) 
     { 
      elements_[i] -= other.elements_[i]; 
     } 
    } 

    friend Point operator+(Point const& a, Point const& b) 
    { 
     Point ret(a); 

     ret += b; 
     return ret; 
    } 

    friend Point operator-(Point const&a, Point const& b) 
    { 
     Point ret(a); 

     ret -= b; 
     return ret; 
    } 

    Point(): elements_() {} 

    Point(int x, int y) 
    { 
     STATIC_ASSERT(nDimensions == 2); 
     elements_[0] = x; 
     elements_[1] = y; 
    } 

    Point(int x, int y, int z) 
    { 
     STATIC_ASSERT(nDimensions == 3); 
     elements_[0] = x; 
     elements_[1] = y; 
     elements_[2] = z; 
    } 
}; 

typedef Point< int, 2 > Point2D; 
typedef Point< int, 3 > Point3D; 

#endif //POINT_H_ 

#include <iostream> 
using namespace std; 

wostream& operator<<(wostream& stream, Point3D const& point) 
{ 
    return (stream << "(" << point[0] << ", " << point[1] << ", " << point[2] << ")"); 
} 

int main() 
{ 
    wcout << "starting" << endl; 
    Point3D a(1, 2, 3); 
    Point3D b(4, 5, 6); 

    a += b; 
    wcout << a << endl; 
} 
+0

謝謝!我只是使用BOOST_STATIC_ASSERT_MSG來獲得更好的錯誤信息。 – user1588625 2012-08-09 21:12:19

+0

一個側面的問題:你爲什麼定義了operator + =和operator- =作爲返回void而不是Point&並返回* this的任何原因? – user1588625 2012-08-09 21:24:10

+0

@ user1588625:我只是沒有理由爲了支持具有多種副作用的表達式而使用更多且可能效率更低的代碼,這在現代軟件工程中是不恰當的。在20世紀70年代,當程序員手工制定優化時,支持多種副作用在C中是有意義的。今天就是編譯器的工作,我們的工作就是編寫清晰且易於理解的代碼。 – 2012-08-09 21:31:51

2

我建議具有類似於類模板,你已經證明了什麼:

template< typename T, int Dimensions> 
class Point { 
}; 

,然後進一步Point2DPoint3D類模板,從繼承:

template <typename T> 
class Point2D : public Point<T,2>{ 
    // add X,Y constructor 
}; 

template <typename T> 
class Point3D : public Point<T,3>{ 
    // add X,Y, Z constructor 
}; 

另一種選擇,如果你有C++ 11的話,就是向通用的Point類模板添加一個可變模板構造函數。這完全避免了繼承,是一般的任何數量的維度:

template< typename T, unsigned int N> 
class Point { 
    template <typename ... Args> 
    Point(const Args& ... args) : elements_{args...} {} 
}; 

這將允許:

Point<int, 3> p3a(1,2,3); // OK, array values set to 1, 2, 3 
Point<int, 3> p3b(1,2); // OK, array values set to 1, 2, 0 
Point<int, 3> p3c(1,2,3,4); // compiler error! 
Point<double, 10> p10a(1,2,3,4,5,6,7,8,9,10); // OK 
+0

沒有必要保護'elements_'嗎?已經提供'operator []'來允許完全訪問基類中的底層存儲。 – 2012-08-09 20:49:21

+0

這工作,輝煌! – user1588625 2012-08-09 20:51:58

+0

@MonroeThomas,贊同! – user1588625 2012-08-09 20:52:58

0

我嘗試了Luchian的答案,但它似乎並沒有提供新的工作專業化的建設者。

但是,您可以使用派生來幫助提供具有不同構造函數的類型。不幸的是,使用這種技術,你必須重新定義你想要公開的構造函數的所有的。另外,請注意,在引用模板類型本身的定義內的當前類型時,不需要重複模板參數。

#ifndef POINT_H_ 
#define POINT_H_ 

template< typename T, int Dimensions = 2 > 
class Point 
{ 
public: 
    typedef typename T value_type; 

    Point() { std::fill(elements_, elements_+Dimensions, 0); } 
    Point(const Point& rhs) : elements_(rhs.elements_) {} 
    virtual ~Point() {} 


    Point & operator=(const Point& rhs) { return *this; } 

    Point operator+(const Point& p) 
    { 
     Point<T, Dimensions> ret; 

     for(int i = 0; i < Dimensions; i++) 
     { 
      ret[i] += elements_[i] + p[i]; 
     } 

     return ret; 
    } 

    Point & operator+=(const Point& p) 
    { 
     for(int i = 0; i < Dimensions; i++) 
     { 
      elements_[i] += p[i]; 
     } 

     return *this; 
    } 

    Point & operator-=(const Point& p) 
    { 
     for(int i = 0; i < Dimensions; i++) 
     { 
      elements_[i] -= p[i]; 
     } 

     return *this; 
    } 

    T & operator[](const size_t index) 
    { 
     return elements_[index]; 
    } 

private: 
    T elements_[Dimensions]; 
}; 


template<typename T> 
class Point2D : public Point<T, 2> 
{ 
public: 

    Point2D() 
    { 
    } 

    Point2D(const Point2D& rhs) : Point<T, 2>(rhs) 
    { 
    } 

    Point2D(const T x, const T y) 
    { 
     (*this)[0] = x; 
     (*this)[1] = y; 
    } 
}; 

template<typename T> 
class Point3D : public Point<T, 3> 
{ 
public: 

    Point3D() 
    { 
    } 

    Point3D(const Point3D& rhs) : Point<T, 3>(rhs) 
    { 
    } 

    Point3D(const T x, const T y, const T z) 
    { 
     (*this)[0] = x; 
     (*this)[1] = y; 
     (*this)[2] = z; 
    } 
}; 


typedef Point2D<int> Point2Di; 
typedef Point3D<int> Point3Di; 



#endif //POINT_H_ 

由於書卷氣的一個點,你可能希望在基類中添加一個常量索引訪問,以及:

const T & operator[](const size_t index) const 
    { 
     return elements_[index]; 
    } 
1

像jahhaj說,常見的代碼轉移到一個基類模板,從它繼承:

template<typename T, int Dimensions> 
class PointBase { 
    ... // all existing code from Point 
}; 

template<typename T, int Dimensions = 2> 
class Point: public PointBase<T, Dimensions> { 
    // empty 
}; 

template<typename T> 
class Point<T, 2>: public PointBase<T, 2> { 
public: 
    Point(T x, T y): PointBase<T, 2>() { // convenience constructor 
     (*this)[0] = x; 
     (*this)[1] = y; 
    } 
}; 

順便說一句,C++ 11個可變參數模板和初始化列表將消除任何需要手工創造便利構造函數。

0

既然你想改變你的構造函數的輸入參數數你有2種方式:
1)將所有實現的基類,並從它推動你的類,然後專門掘進類
2)使用預處理器命令重複構造函數(例如BOOST_PP_REPEAT),然後使用模板(tr1::disable_ifboost::disabled_if)禁用無效的構造函數。

第一的技術方法很簡單,但對於第二個有大量的文檔資料和幫助有關BOOST_PP_REPEAT和「disable_if`,如果他們不幫你讓我知道送你一個完整的工作代碼