Visualization Tool Kit(VTK)など

幾何のベクトルクラスを改めて開発してみた。
  • 演算子のオーバーロード (線形計算)
  • 固定次元(テンプレート)
  • クロス積(2次元・3次元)

ここで、
  • 次元に関わらず共通処理(線形計算、ドット積、ノルム、要素アクセス、etc.)
  • 次元によって異なる処理(クロス積)
の実装が問題となる。

「共通する部分を親クラスに実装し、次元ごとに派生クラスを作る」が一般的な手法であるが、
例えば下記のようなコードにおいて冗長な記述が必要となる。

class Vec2D : public Vec_<double, 2>
{
  public:
    // 親クラスを受け入れるコンストラクタが必要
    Vec2D(Vec_<double,2>& v) : Vec_<double,2>(v) {}; 
    double cross(Vec2D const& v);
};
class Vec3D : public Vec_<double, 3>
{
  public:
    Vec3D(Vec_<double,3>& v) : Vec_<double,3>(v) {};
    Vec3D cross(Vec3D const& v);
};

Vec2D v1(3,5), v2(5,6), v3(1,2);
//double result = (v1+v2).cross(v3); // error: operator+の戻り値はVec_<T,N>
double result = Vec2D(v1+v2).cross(v3);
Vec3D v4(3,5,1), v5(5,6,2), v6(1,2,3);
Vec3D v7 = Vec3D(v4+v5).cross(v6);

派生クラスに次元に特化したメンバー関数を定義しても、親クラスで定義した共通部分の結果は当然親クラスで返される。
このような場合、テンプレートの特殊化で対応すれば派生クラスを作る必要はなく、上記冗長性も生じない。

まず思いつくのは


template<typename T, size_t N>
class Vec_
{
  public:
    template<typename T2>
    T2 cross(Vec_ const& v);
};


しかし、特殊化部分を記述しようとするとつまずく。

/* ill-form
template<T>
template<>
T Vec_<T,2>::cross<T>(Vec_<T,2> const& v) {  }
*/

関数テンプレートは部分特殊化できない。また戻り値をどう記述してよいかわからない。

このような場合固有処理を外部クラスに定義すればよい。

template<typename VectorType>
struct compute_cross
{
  typedef void return_type; // クロス積は2次元と3次元のみ
};

template<typename T>
struct compute_cross<Vec_<T,2>>
{
  typedef T return_type;
  return_type apply(Vec_<T,2> const& v1, Vec_<T,2> const& v2) { // }
};

template<typename T>
struct compute_cross<Vec_<T,3>>
{
  typedef Vec_<T,3> return_type;
  static inline return_type apply(Vec_<T,3> const& v1, Vec_<T,3> const& v2) { // }
};

template<typename T, size_t N>
class Vec_
{
  public:
    compute_cross<Vec_>::return_type cross(Vec_ const& v) const
    {
      return compute_cross<Vec_>::apply(*this, v);
    }
};


また、2次元と3次元以外にはcompute_cross::applyが定義されていないが、Vec_::crossを呼び出さない限りエラーとはならない。

typedef Vec_<double,3> Vec3D;
typedef Vec_<double, 4> Vec4D;
Vec3D v1, v2;
Vec4D v3, v4; // ok

std::fill(v1.begin(), v1.end(), 1); // v1(1,1,1)
std::fill(v2.begin(), v2.end(), 2); // v2(2,2,2)
std::fill(v3.begin(), v3.end(), 1); // v3(1,1,1,1)
std::fill(v4.begin(), v4.end(), 2); // v4(2,2,2,2)

v1+v2; // ok
v1.dot(v2); // ok
v1.cross(v2); // ok

v3+v4; // ok
v3.dor(v4); // ok
v3.cross(v4); // error: compute_cross<Vec_<double,4>>::applyがない

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu

メニュー

チュートリアル

アルゴリズム(数学)

並列計算

STL

#include<memory> #include<string> #include<sstream> #include<algorithm> #include<functional> #include<numeric>

Media Foundation

【メニュー編集】
Wiki記法ガイド

メンバーのみ編集できます