2009-08-09 20 views
3

私は、テンプレートパラメータによって決定されるサイズの行列クラスを持っています。C++クラステンプレートで無限再帰を避ける方法

template <unsigned cRows, unsigned cCols> 
class Matrix { 
    ... 
}; 

私のプログラムは、いくつかのサイズの行列、通常の2x2、3x3の、および4x4のを使用しています。ランタイムパラメータではなくテンプレートパラメータで行列サイズを設定することにより、コンパイラは多くのインライン化と最適化を実行できます。

今、私は、1つ少ない行と1つ少ない列を持つ新しい行列を返すメンバ関数が必要です。

Matrix<cRows - 1, cCols - 1> Reduced(unsigned row, unsigned col) const { ... } 

考えられるのは、指定された行と列が削除された行列が返されるということです。実際には、少なくとも3行3列の行列を持ち、最小2x2を返す行列でのみ呼び出されます。

コンパイラは下限を認識しないため、無限に再帰的にスタックされ、縮小するサイズのテンプレートをインスタンス化しようとします。

Matrix<cRows - 1, cCols - 1> Reduced(unsigned row, unsigned col) const { 
    static_assert(cRows > 1 && cCols > 1); 
    if (cRows <= 1 || cCols <= 1) throw std::domain_error(); 
    Matrix<cRows - 1, cCols - 1> r; 
    // ... initialize r ... 
    return r; 
} 

どちらstatic_assertif -statementは、0x0の行列が生成されることはありませんコンパイラに十分に強い手がかりのようです:私はこれらの小さいサイズが発生することができないという機能自体に2つの手がかりを入れてみました。 (皮肉なことに、それはコンパイル時の条件が一定のifの文章について文句を言う)

このコンパイル時の無限再帰を避ける方法はありますか?

答えて

12

行がないか、列がない行列の特殊化を行う必要があります。

など。 ( - 1、cCols -1カラス)

template<unsigned cRows> 
class Matrix< cRows, 0 > 
{ 
    Matrix<cRows - 1, 0> Reduced() { return Matrix<cRows - 1, 0>(); } 
}; 


template<unsigned cCols> 
class Matrix< 0, cCols > 
{ 
    Matrix<0, cCols - 1> Reduced() { return Matrix<0, cCols - 1>(); } 
}; 


template<> 
class Matrix< 0, 0 > 
{ 
    Matrix<0, 0> Reduced() { return Matrix<0, 0>(); } 
}; 

あなたが持っている問題は、テンプレートパラメータの特定のセットとマトリックスの削減機能をインスタンス化しようとすると、常に異なるパラメータセットのためのマトリックステンプレートをインスタンス化する必要ということです。この再帰はどこかで停止しなければならない。唯一の正方行列を扱っているなら、より少ない専門化で逃げることができます。

また、完全に空のクラス、たとえば1x1の行列を使用しない場合は、2x2行列のreduceの結果として再帰を停止することができます。

template<> 
class Matrix< 1, 1 > {}; 
+0

彼は2x2を最も小さく言った、私はまだこれが最高だと思う。おそらく 'const int MinimumRows'、' const int MinimumColumns'を追加して調整可能にしてください。 – GManNickG

+0

ありがとう、それはトリックでした。私はそれをさらに進化させ、非還元機能を「非還元」にして、専門化を分離しやすくしました。 –

0

再帰を終了させる場合の動作を明示的に指定する必要があります。詳細は、this DDJ articleを参照してください。

template<int n> 
class META_FACTORIAL 
{ 
public: 
    enum{ 
    RET = n * META_FACTORIAL<n-1>::RET 
    }; 
}; 

template<> 
class META_FACTORIAL<0> 
{ 
public: 
    enum{ RET = 1 }; 
}; 
1

あなたは、そのメソッドが含まれていないカラスやcColsの小さな値のテンプレート特殊化を指定することができます。ここでは記事からの簡単な例です。

1

コンパイル時と実行時の動作がちょっと混乱しているようですが、私はあなたのコードでは少し混乱しますが、あなたが望むのは、値0、0のテンプレートの特殊化です再帰。

あなたはすでにそれを持っていない場合は、私は詳細にこの種のものをカバーVandervoorde & Josuttis氏によって C++ Templates: The Complete Guideを読んで示唆しています。

0

よりもむしろ再帰を終了するには、クラス全体を特化し、別のオプションは、行列のサイズは2×2を超えている場合にのみ、それを利用できるようにする機能にboost::enable_ifを使用するかもしれません。

関連する問題