Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid infinite recursion in C++ class templates

I have a matrix class with the size determined by template parameters.

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

My program uses matrices of a few sizes, typically 2x2, 3x3, and 4x4. By setting the matrix size with template parameters rather than run-time parameters allows the compiler to do a lot of inlining and optimization.

But now I need a member function that returns a new matrix that has one fewer row and one fewer column.

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

The idea is that that it will return a matrix with the specified row and column deleted. In practice, this will only ever be called with a matrix that has at least three rows and three columns, returning a 2x2 at the smallest.

The compiler doesn't see the lower bound, so it gets stuck in an infinite recursion trying to instantiate the templates with ever decreasing sizes. I tried putting two clues in the function itself that these smaller sizes cannot occur:

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;
}

Neither the static_assert nor the if-statement seems to be a strong enough clue to the compiler that a 0x0 matrix will never be generated. (Ironically, it does complain about the if-statement having a constant compile-time condition.)

Does anyone have any suggestions on how to avoid this compile-time infinite recursion?

like image 903
Adrian McCarthy Avatar asked Nov 04 '25 17:11

Adrian McCarthy


2 Answers

You need to provide a specialization for a Matrix that has no rows or no columns.

E.g.

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>(); }
};

The issue you have is that attempting to instantiate the Matrix Reduced function with a particular set of template parameters always required instantiating the Matrix template for a different set of parameters (cRows - 1, cCols -1). This recursion has to be stopped somewhere. If you are only ever dealing with square matrices, then you can get away with fewer specializations.

Also, you can could stop the recursion with a completely empty class if you are never going to use, say, a 1x1 matrix, the result of reduce on a 2x2 matrix.

template<>
class Matrix< 1, 1 > {};
like image 195
CB Bailey Avatar answered Nov 06 '25 08:11

CB Bailey


You can specify a template specializations for small values of cRows or cCols which does not include that method.

like image 24
sepp2k Avatar answered Nov 06 '25 08:11

sepp2k



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!