次のようなものがあるとします。
class Shape // base class
{
private:
bool degenerate, ill_defined;
...
public:
bool isVoid () { return false; }
bool isCircle() { return false; }
bool isPoint () { return false; }
bool isPlane () { return false; }
bool isSphere() { return false; }
...
};
class Void : public Shape {
...
}
class Plane : public Shape
{
public:
bool isPlane() { return !degenerate && !ill_defined; }
bool isVoid () { return ill_defined; }
...
operator Void () throw() {
if (isVoid()) return Void();
else throw ...; //some error
}
...
}
class Point : public Shape {
private:
double radius;
...
public:
bool isPoint() { return !ill_defined; }
bool isVoid () { return ill_defined; }
...
operator Void () throw() { ... }
...
}
class Circle : public Shape // similar to the rest
class Sphere : public Shape // similar to the rest
Plane
aと aの交点は、次のSphere
いずれかになります。
- a
Circle
(平面が球を「切断」する場合) - a
Point
(平面が球体に「ちょうど接触」している場合) - a
Void
(球が完全に平面の上または下にある場合)
Plane
aと aの交差をどのように定義して使用するのが最善なのか疑問に思っていましSphere
た。
intersect(const Sphere& S, const Plane& P)
method/free 関数はコンパイル時に不明です。
これまでにこのような状況に遭遇したことはなかったので、いくつかの可能な方法を調べました。を推奨するこの質問に出くわしましたboost::variant
。私の状況では、それは次のようになります
boost::variant<Void, Point, Circle> intersection =
intersect(const Sphere& S, const Plane& P);
しかし、これには 3 つの欠点があります。
- 醜いです。
andには.
intersection.radius
_ あなたは次のようなことをしなければならないでしょうPoint
Void
radius
if (intersection.isPoint()){ ... } else if (intersection.isCircle()) { // possibly cast to Point if degenerate, otherwise: double R = intersection.radius; ... } // etc.
これらすべての形状を実装するライブラリのユーザーは、2 つの形状が交差することによって返される型を常に把握しておく必要があります。つまり、ユーザーは常に、
boost::variant<scope::Void, scope::Point, scope::Circle>
複雑で単純に醜い型を宣言する必要があります。幸いなことに、c++11 にはそのauto
ためのキーワードがあります。または、そのようなメンバーを使用することもできますclass Sphere : public Shape { ... public: boost::variant<scope::Void, scope::Point, scope::Circle> intersect_type; intersect_type intersect(const Plane& P); ... };
使えるように
Sphere::intersect_type t = S.intersect(P);
どこ
S
で のインスタンスSphere
とP
のインスタンスですPlane
。それでも、すべての可能なタイプを個別に処理する必要があります。if (intersection.isPoint()){ ... } else if (intersection.isCircle()){ intersection.radius; } // etc.
そのため、ユーザーから取り除こうとした複雑さは実際にはまだ残っています。
ここで何かが足りない気がします。おそらく、私の基底クラスを実装するためのよりスマートな方法はありShape
ますか? それとも、別の専用Intersect
クラスを作成する必要がありますか? この状況で最もエレガントで効率的かつ効果的なソリューションはどれですか?