1

私は行列クラスを書いています。この定義を見てください:

template <typename T, unsigned int dimension_x, unsigned int dimension_y> 
class generic_matrix
{
  ...
  generic_matrix<T, dimension_x - 1, dimension_y - 1> 
  minor(unsigned int x, unsigned int y) const
  { ... }
  ...
}

template <typename T, unsigned int dimension> 
class generic_square_matrix : public generic_matrix<T, dimension, dimension>
{
  ...
  generic_square_matrix(const generic_matrix<T, dimension, dimension>& other)
  { ... }
  ...
  void foo();
}

generic_square_matrix クラスは、行列の乗算などの追加機能を提供します。これを行うことは問題ありません:

generic_square_matrix<T, 4> m = generic_matrix<T, 4, 4>();

コンストラクターにより、タイプが generic_square_matrix でなくても、任意の正方行列を M に割り当てることができます。これが可能なのは、サポートされている関数のみで、データが子全体で変更されないためです。これも可能です:

generic_square_matrix<T, 4> m = generic_square_matrix<T, 5>().minor(1,1);

ここでも同じ変換が適用されます。しかし、ここで問題が発生します。

generic_square_matrix<T, 4>().minor(1,1).foo(); //problem, foo is not in generic_matrix<T, 3, 3>

これを解決するには、generic_square_matrix::minor が generic_matrix の代わりに generic_square_matrix を返すようにします。これを行う唯一の方法は、テンプレートの特殊化を使用することだと思います。しかし、特殊化は基本的に別のクラスのように扱われるため、すべての関数を再定義する必要があります。派生クラスの場合とは異なり、特殊化されていないクラスの関数を呼び出すことはできないため、関数全体をコピーする必要があります。これはあまり優れたジェネリック プログラミング ソリューションではなく、多くの作業が必要です。

C ++には、私の問題に対する解決策がほとんどあります。派生クラスの仮想関数は、基本クラスが返すクラスから派生したクラスである場合、基本クラスが返すものとは異なるクラスへのポインターまたは参照を返すことができます。generic_square_matrixgeneric_matrixから派生していますが、関数はポインターも参照も返さないため、ここでは当てはまりません。

この問題の解決策はありますか (まったく別の構造が関係している可能性があります。私の唯一の要件は、次元がテンプレート パラメーターであり、正方行列に追加機能を持たせることができることです)。

前もって感謝します、

ルード

4

4 に答える 4

4

仮想化せずに、派生クラスに関数を実装するだけです。これにより、基本クラスの実装が「隠蔽」され、メンバー関数を非表示にすることは一般的に嫌われますが、使用するには望ましい場合があります。

別の名前を使用する方がよい方法かもしれませんが、それによってインターフェイスの「純度」が損なわれる可能性があります。

最後に、minor() をメンバー関数ではなくフリー関数として実装できますか? 次に、必要に応じてオーバーロードを指定すると、コンパイル時に正しい関数が呼び出されます。これは、私が最初に使用したメソッド隠蔽のケースに最も近いものですが、「良い習慣」としてより一般的に受け入れられるのではないかと思います。

于 2010-05-26T16:56:55.273 に答える
3

minor()単純に自由な関数を作成できるのであれば、設計を複雑にする必要はありません。

template<typename T, unsigned int X, unsigned int Y> 
generic_matrix<T, X-1, Y-1> 
minor(const generic_matrix<T, X, Y>& m, unsigned int x, unsigned int y);

...その後、必要なだけ代替バージョンを追加できます。

于 2010-05-26T17:06:39.243 に答える
1

これらの型をポリモーフィックに使用しない場合は、派生クラスの基本クラス メソッドを非仮想的にオーバーライドして、派生クラス オブジェクトを返すことができます。通常は望ましくない基本クラスのバージョンを非表示にしますが、あなたの場合はそうです。

または、必要に応じて、さまざまな種類の行列のオーバーロードを使用して、これらのメンバー関数を関数テンプレートから解放します。(ただし、テンプレート化によって自動的に処理される場合があります。)

于 2010-05-26T17:07:14.473 に答える
0

テンプレートの特殊化が機能します。共有コードを取得して基本クラスに配置し、両方から継承するだけです。ベースにデストラクタを保護して、ベースにキャストして削除しようとすることでコンセプトを壊すことがないようにします。

そのようなもの:

namespace detail_
{
  template < typename T >
  struct my_templates_common_functionality
  {
    void f1() {}
    void f2() {}
  protected:
    ~my_templates_common_functionality() {}
  };
}

template < typename T >
struct my_template : detail_::my_templates_common_functionality<T>
{
  int f3() { return 5 }
};

template < >
struct my_template<double> : detail_::my_templates_common_functionality<double>
{
  char* f3() { return nullptr; }
};

他にも方法はありますが、これが最も簡単な方法の 1 つです。

于 2010-05-26T16:58:30.230 に答える