3

いくつかの新しいc++11機能に慣れるために、テンプレート化された数学行列クラスを作成しています。基本的な宣言は次のとおりです。

template <typename Type, int kNumRows, int kNumCols>
class Matrix { ... };

このクラスには、その小行列式の1つを返すメンバー関数があります(これは後でNxN行列の行列式を計算するために使用されます)。

Matrix<Type, kNumRows - 1, kNumCols - 1> minor(const int row, const int col) {
  static_assert(kNumRows > 2, "");
  static_assert(kNumCols > 2, "");

  ...
}

次に、正方行列の行列式を計算するための非メンバー関数を作成しました。

template <typename Type, int kSize>
Type determinant(const Matrix<Type, kSize, kSize>& matrix) {
  switch (kSize) {
  case 2:
    return 0; // For now unimportant
  case 3:
    // Recursively call the determinant function on a minor matrix
    return determinant(matrix.minor(0, 0));
  }
  ...
}

main()で、3x3行列を作成し、それを呼び出しますdeterminantこれはコンパイルされません。コンパイラは事実上ケース3に移行し、マイナーマトリックスを作成して呼び出しdeterminantます。次に、case 3再びステップインし、1x1マイナーを作成しようとしてstatic_assertになります。

質問は簡単です:私はここで何かが欠けていますか?このようなテンプレート関数を再帰的に呼び出すことは単に許可されていませんか?これはコンパイラの障害ですか(私はそれを疑っています)?

完全を期すために:私はClang++を使用しています。

4

3 に答える 3

3

コンパイラは、実行中にすべてのコード パスを参照しない場合でも、すべてのコード パスを生成します (最適化ステップで実際に削除される場合があります)。結果として、 3 未満determinant<Type, kSize - 1, kSize - 1>の場合でも、常にインスタンス化されますkSize

これを防ぐには、関数を部分的に特殊化する必要があります。determinant関数を適切にオーバーロードする必要があります。

template <typename Type>
Type determinant(const Matrix<Type, 2, 2>& matrix) {
  ...
}

ちなみに、これにより、switch関数内のステートメントが冗長になります。

于 2012-12-11T12:56:09.547 に答える
3

テンプレートはコンパイル時の処理をswitch決定しますが、ステートメントは実行時の処理を決定します。コンパイラは、コンパイル時に正しいケースが「明白」であっても、すべてのスイッチ ケースに対してコードを生成するか、少なくとも有効性を検証します。

を使用する代わりにswitch、行列式をオーバーロードしてみてください。

template <typename Type>
Type determinant(const Matrix<Type, 1, 1>& matrix) {
    return matrix(0,0);
}

template <typename Type>
Type determinant(const Matrix<Type, 2, 2>& matrix) {
    return 0; // (incorrect math)
}

template <typename Type, int kSize>
Type determinant(const Matrix<Type, kSize, kSize>& matrix) {
    return determinant(matrix.minor(0,0)); // (incorrect math)
}
于 2012-12-11T13:00:09.423 に答える
1

テンプレートの特殊化を使用して、コンパイル時に切り替えを行う必要があります。

template <typename Type, int kSize>
struct Determinate {
    Type operator()(const Matrix<Type, kSize, kSize>& matrix) const {
        // Recursively call the determinant function on a minor matrix
        return Determinate<Type, kSize-1>{}(matrix.minor(0, 0));
    }
};
template <typename Type>
struct Determinate<Type, 2> {
    Type operator()(const Matrix<Type, kSize, kSize>& matrix) const {
        return 0; // For now unimportant
    }
};
template <typename Type, int kSize>
Type determinant(const Matrix<Type, kSize, kSize>& matrix) {
    return Determinate<Type, kSize>{}(matrix);
}
于 2012-12-11T12:56:17.230 に答える