键入以用作模板参数来比较双精度值

时间:2010-04-28 16:09:46

标签: c++ templates

我得到了这个n维点对象:

template <class T, unsigned int dimension> class Obj {
protected:
    T coords[dimension];
    static const unsigned int size = dimension;
public:
    Obj() { };
    Obj(T def) { for (unsigned int i = 0; i < size; ++i) coords[i]=def; };
    Obj(const Obj& o) { for (unsigned int i = 0; i < size; ++i) coords[i] = o.coords[i]; }
    const Obj& operator= (const Obj& rhs) { if (this != &rhs) for (unsigned int i = 0; i < size; ++i) coords[i] = rhs.coords[i]; return *this; }


    virtual ~Obj() {  };
    T get (unsigned int id) { if (id >= size) throw std::out_of_range("out of range"); return coords[id]; }
    void set (unsigned int id, T t) { if (id >= size) throw std::out_of_range("out of range"); coords[id] = t; }

};

和使用Obj作为基类的3D点类:

template <class U> class Point3DBase : public Obj<U,3> {
    typedef U type;

public:
    U &x, &y, &z;
public:
    Point3DBase() : x(Obj<U,3>::coords[0]), y(Obj<U,3>::coords[1]), z(Obj<U,3>::coords[2]) { };
    Point3DBase(U def) : Obj<U,3>(def), x(Obj<U,3>::coords[0]), y(Obj<U,3>::coords[1]), z(Obj<U,3>::coords[2]) { };
    Point3DBase(U x_, U y_, U z_) : x(Obj<U,3>::coords[0]), y(Obj<U,3>::coords[1]), z(Obj<U,3>::coords[2])  { x = x_; y = y_; z= z_; };
    Point3DBase(const Point3DBase& other) : x(Obj<U,3>::coords[0]), y(Obj<U,3>::coords[1]), z(Obj<U,3>::coords[2]) { x = other.x; y = other.y; z = other.z; }
// several operators ...
};

运算符,基本上是用于比较的运算符,使用简单的compare-the-member-object方法,如:

friend bool operator== (const Point3DBase<U> &lhs, const Point3DBase<U> rhs) { return (lhs.x ==  rhs.x && lhs.y == rhs.y && lhs.z == rhs.z); }

然后我发现,对于双值的比较,简单相等方法不是很有用,因为双值应该与误差范围进行比较。引入误差范围的最佳方法是什么?我认为epsDouble类型作为模板参数,但我无法弄清楚如何实现这一点。

编辑:

我见过链式输出流运算符,它调用输出流运算符类型的输出流运算符...有没有办法将比较委托给表示浮点类型的自定义类型?

3 个答案:

答案 0 :(得分:3)

如果你想为给定浮动类型的所有实例提供一个epsilon值,它实际上非常简单:

template <>
bool operator<(const Point3DBase<double>& lhs, const Point3DBase<double>& rhs)
{
}

如果没有,那么我将引导您走向基于政策的设计,正如Alexandrescu所示:

namespace detail
{
  template <class U>
  struct DefaultComparator: std::binary_function<bool, U, U>
  {
    bool operator()(U lhs, U rhs) const { return lhs < rhs; }
  };
}

template < class U, class Comparator = detail::DefaultComparator<U> >
class Point3DBase;

template < class U, class C>
bool operator<(Point3DBase<U,C> const& lhs, Point3DBase<U,C> const& rhs)
{
  return C()(lhs,rhs);
}

请注意,您仍然可以通过专门化DefaultComparator

来定义安全默认值
namespace detail
{
  template <>
  struct DefaultComparator<float> {};

  template <>
  struct DefaultComparator<double> {};
}

使用此定义,如果不自行传递operator==参数,则无法使用Comparator。另一种解决方案是允许它,但在上面两个专业化的定义中提供默认的epsilon。

所有其他操作(>, <=, >=, ==, !=)可以从<中轻松推导出来(虽然可能效率不高),例如派生自boost::equality_comparable和/或boost::less_than_comparable。< / p>

答案 1 :(得分:1)

是的,您不能拥有浮点类型的模板参数。您可以使用int参数,告诉epsilon值的负10基本日志,例如值为3意味着0.001。

答案 2 :(得分:1)

如果你想掌握epsilon,你可以定义一个double的包装类,它在比较这个类的实例时会处理这个epsilon。通过这种方式,您可以使用在数字类型上模板化的任何其他函数或类。

template <int EPS>
class DoubleEps {
  double val;
public:
  operator double {return val;}
  DoubleEps(double d) : val(d) {}
  operator==(DoubleEps d) {
    // take care of epsilon 
  }
  // other operators if needed
};