幾何のベクトルクラスを改めて開発してみた。
ここで、
「共通する部分を親クラスに実装し、次元ごとに派生クラスを作る」が一般的な手法であるが、
例えば下記のようなコードにおいて冗長な記述が必要となる。
派生クラスに次元に特化したメンバー関数を定義しても、親クラスで定義した共通部分の結果は当然親クラスで返される。
このような場合、テンプレートの特殊化で対応すれば派生クラスを作る必要はなく、上記冗長性も生じない。
まず思いつくのは
しかし、特殊化部分を記述しようとするとつまずく。
関数テンプレートは部分特殊化できない。また戻り値をどう記述してよいかわからない。
このような場合固有処理を外部クラスに定義すればよい。
また、2次元と3次元以外にはcompute_cross::applyが定義されていないが、Vec_::crossを呼び出さない限りエラーとはならない。
- 演算子のオーバーロード (線形計算)
- 固定次元(テンプレート)
- クロス積(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がない
コメントをかく