如何返回穿越线的点?线条并不总是相交

时间:2013-03-26 09:42:27

标签: c++ function exception return return-value

假设我们想要创建一个计算两条线交点的函数。交点不总是定义的或唯一的。如何在函数的签名中反映出来?

我想出了这些选择:

  1. bool getIntersectionPoint ( Line& a, Line& b , Point& result );

    如果行是并行的,则返回false。否则返回true并将结果写入变量。

  2. Point getIntersectionPoint ( Line& a, Line& b );

    如果行是平行的,则抛出异常。

  3. [更新]
    如果我们创建2个函数bool doLinesIntersect(const Line&, const Line&);Point twoLinesIntersection(const Line&, const Line&);,则第一个函数在第一个函数返回false后仍然可以调用。

7 个答案:

答案 0 :(得分:4)

恕我直言,直线交叉产生对象,这就是为什么拥有

是诚实的

boost::variant<Empty, Point, Line> intersect(Line const & l1, Line const & l2)

和辅助函数,如

boost::optional<Point> getIntersectionPoint(Line const & l1, Line const & l2)

bool isParallel(Line const & l1, Line const & l2)

修改 如果您不想使用boost库,您可以轻松创建简单的类似物:

struct intersection_result_t
{
  enum isec_t
  {
    isec_empty, isec_point, isec_line
  }

  intersection_result_t()
    : type_(isec_empty)
  {
    new (storage_) Empty();
  }

  intersection_result_t(Empty const & e)
    : type_(isec_empty)
  {
    new (storage_) Empty(e);
  }
  intersection_result_t(Point const & p)
    : type_(isec_point)
  {
    new (storage_) Point(p);
  }
...
  intersection_result_t(intersection_result_t & ir)
    : type_(ir.type_)
  {
    switch(ir.type_)
    {
      case isec_empty:
        new (storage_) Empty(*static_cast<Empty*>(ir.storage_));
      case ....
    }
  }
private:
  void destroy()
  {
    switch(type_)
    {
      case isec_empty:
        operator delete (static_cast<Empty*>(storage_), storage_);
      case ....
    }
  }
private:
  char storage_[MAX(sizeof(Empty), sizeof(Point), sizeof(Line))];
  isec_t type_;
};

等等需要更多的交换机。或者您可以使用模板。 对于可选项,只需使用initialized_代替type_来跟踪构建状态。

答案 1 :(得分:3)

根据 ulidtko 的建议,返回“可能是Point”的对象会很好。在C ++中,您可以使用boost::optional

boost::optional<Point> getIntersectionPoint(const Line& a, const Line& b) {
    // ...
    if (there_is_zero_or_inifinty_points_of_intersection)
        return boost::optional<Point>();
    else
        return boost::optional<Point>(the_point_of_intersection);
}

您可以将boost::optional<Point>视为Point*。特别是,客户端可以通过这种方式查询返回的交叉点是否正确:

boost::optional<Point> point = getIntersectionPoint(a, b);
if (point)
    // point "points to" a proper Point which can be retrieved as *point
else
    // point is "NULL", that is, there's no unique point of intersection

有趣的是,boost::optional的激励范例也是一个几何问题。这不是巧合,因为我相信boost::optional作者写了几何软件。 ; - )

值得一提的是,在下一版C ++标准中,有一个proposaloptional包含在STL中。

答案 2 :(得分:0)

平行线不是错误或意外。因此抛出异常是不合适的。

BTW这是一个优选的函数签名。

bool getIntersectionPoint(const Line& a, const Line& b, Point& result);

指定const使得该函数不会修改它的前两个参数,并且还允许您使用临时函数调用该函数。

答案 3 :(得分:0)

从抽象(API)的角度来看,您有两个不相关的函数:

bool doLinesIntersect(const Line&, const Line&);

Point twoLinesIntersection(const Line&, const Line&);

第二个函数必须假设线实际上相交(并且不共线)。 如果您不信任您的呼叫者,您可能希望抛出一个异常,表明不满足前提条件。

答案 4 :(得分:0)

你的第二个功能应该不会返回Point&amp;但是Point值(谁拥有它?)

或者,有第三种选择:

Point getIntersectionPoint ( Line& a, Line& b, bool* ok );

如果你为'ok'提供一个NULL指针,如果没有交叉则抛出,否则在'ok'的值中返回false。

我建议对于这样的函数,最好完全避免异常。一个非交叉点实际上并不是那么特殊,并且异常应该保留为意外的东西。你可以期待不相交的线。

使用返回bool的版本,或带有bool参数但不抛出的版本。

EDIT 经常使用的第四种选择:

std::pair<bool, Point> getIntersectionPoint ( Line& a, Line& b );

答案 5 :(得分:0)

这个问题是在C ++中更轻松 sum types 的一个很好的动机。

在像Haskell这样的语言中,你的函数会有以下签名:

getIntersectionPoint :: Line -> Line -> Maybe Point

其中Maybe Point(函数的返回类型)实质上是指一个类型,它可以有两个值:NothingJust p,其中p是{{1 }}

此类简单和类型的可用性实际上会使问题变得无关紧要,因为所有方法都会合并为一个。


编辑:this answer很好地证明了Boost提供了简单的和类型设施。有boost::optionalboost::variant。甜。

答案 6 :(得分:-1)

如果不考虑上下文,人们将毫无结束地讨论。

Supouse你想在一些

中使用这个功能
fillWithColor(color c, set_of lines& figure);

并以某种方式使用getLinesIntersection来做到这一点。如果你需要检查每个电话,不仅会使你的代码变得混乱,而且你不知道如何处理错误。简单地使用该函数并让调用者捕获异常。

在其他情况下,您可以实施:

bool doLinesIntersect(const Line&, const Line2&, Point &p);
Point getLinesIntersection(const Line&, const Line2&)
{
   Point p;
   If (! doLinesIntersect(Line, Line2,p) throw …;
   return p;
}

两种方法都非常有效!!!