2012-08-09 79 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`,如果他们不帮你让我知道送你一个完整的工作代码