假设我们想要创建一个计算两条线交点的函数。交点不总是定义的或唯一的。如何在函数的签名中反映出来?
我想出了这些选择:
bool getIntersectionPoint ( Line& a, Line& b , Point& result );
如果行是并行的,则返回false。否则返回true并将结果写入变量。
Point getIntersectionPoint ( Line& a, Line& b );
如果行是平行的,则抛出异常。
[更新]
如果我们创建2个函数bool doLinesIntersect(const Line&, const Line&);
和Point twoLinesIntersection(const Line&, const Line&);
,则第一个函数在第一个函数返回false后仍然可以调用。
答案 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 ++标准中,有一个proposal将optional
包含在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
(函数的返回类型)实质上是指一个类型,它可以有两个值:Nothing
或Just p
,其中p
是{{1 }}
此类简单和类型的可用性实际上会使问题变得无关紧要,因为所有方法都会合并为一个。
编辑:this answer很好地证明了Boost提供了简单的和类型设施。有boost::optional
和boost::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;
}
两种方法都非常有效!!!