Класс Templated Matrix не генерирует функции правильно - PullRequest
2 голосов
/ 19 августа 2010

По сути, у меня есть такой класс матрицы (с большим количеством перегрузок операторов и удалением других функций):

template
<
    uint32 TRows,
    uint32 TCols
>
struct Matrix
{
    float values[TRows][TCols];

    inline explicit Matrix()
    {
    }

    inline Matrix<TRows - 1, TCols - 1> minor(const uint32 col, const uint32 row)
    {
        Matrix<TRows - 1, TCols - 1> matrix;
        for(int i = 0; i < TRows; ++i)
            for(int j = 0; j < TCols; ++j)
            {
                if(i == col || j == row) continue;
                matrix.values[i - (i > col)][j - (j > row)] = this->values[i][j];
            }
        return matrix;
    }

    inline float determinant()
    {
        if(TRows != TCols) throw DimensionError("Matrix is not square");

        float det = 0;

        if(TRows <= 0)
            det = 0;
        else if(TRows == 1)
            det = this->values[0][0];
        else if(TRows == 2)
            det = this->values[0][0] * this->values[1][1] - this->values[1][0] * this->values[0][1];
        else
            for(int j = 0; j < TCols; ++j)
                det += (j % 2 ? -1 : 1) * this->values[0][j] * this->minor(0, j).determinant();

        return det;
    }
}

Я не понимаю, почему для строки det += (j % 2 ? -1 : 1) * this->values[0][j] * this->minor(0, j).determinant(); GCC пытается сгенерировать огромное количество функций:

matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -49u, unsigned int TCols = -49u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -48u, unsigned int TCols = -48u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -47u, unsigned int TCols = -47u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -46u, unsigned int TCols = -46u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -45u, unsigned int TCols = -45u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -44u, unsigned int TCols = -44u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -43u, unsigned int TCols = -43u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -42u, unsigned int TCols = -42u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -41u, unsigned int TCols = -41u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -40u, unsigned int TCols = -40u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -39u, unsigned int TCols = -39u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -38u, unsigned int TCols = -38u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -37u, unsigned int TCols = -37u]'
matrix.cpp:95:   instantiated from `float Matrix<TRows, TCols>::determinant() [with unsigned int TRows = -36u, unsigned int TCols = -36u]'

Вероятно, это какая-то ошибка в моем коде, но я не могу понять, в чем причина моей ошибки. Помощь будет высоко ценится!

Ответы [ 2 ]

3 голосов
/ 19 августа 2010

Matrix :: minor (0, j) возвращает матрицу размера (N-1, N-1). Вызов его определителя делает процесс рекурсивным, что означает, что вы генерируете вспомогательный (и метод определителя) для всех p целых чисел, начиная с N до ... -infinity?

Вы добавили специализацию для случая, когда N == 1 или N == 0? Вы должны убедиться, что рекурсия останавливается!

Попробуйте добавить что-то вроде:

template<>
struct Matrix<1,1>
{
  float values[1][1];

  float determinant(){ return values[0][0]; }
};

РЕДАКТИРОВАТЬ: Рекурсия должна быть остановлена ​​во время компиляции. Добавление оператора if внутри метода не мешает компилятору компилировать все возможные пути.

0 голосов
/ 19 августа 2010

Я согласен с "Benoît"

Тот факт, что во время выполнения вы никогда не вызываете функцию minor для «пустой» матрицы - не мешает компилятору генерироватьэтот код.

Чтобы избежать этого, вы должны играть с явными специализациями шаблонов.Так что даже во время компиляции компилятор не встретит программных путей, которые приводят к бесконечной генерации шаблонных функций.

Как этот:

template <int N>
float determinantInternal()
{
    // Standard algoritm for NxN matrix
    float det = 0;
    for(int j = 0; j < TCols; ++j)
        det += (j % 2 ? -1 : 1) * this->values[0][j] * this->minor(0, j).determinant();

    return det;
}

template <>
float determinantInternal<0>()
{
    return 0;
}

float determinantInternal<1>()
{
    return this->values[0][0];
}

float determinantInternal<2>()
{
    return this->values[0][0] * this->values[1][1] - this->values[1][0] * this->values[0][1];
}

template <>
float determinant()
{
    if (TRows != TCols)
        throw DimensionError("Matrix is not square");
    // Alternatively you can use something to prevent this from compiling. 
    // This way you produce the compile-time error if attempted to call
    // determinant on a non-square matrix
    BOOST_STATIC_ASSERT(TRows == TCols);

    return determinantInternal<TRows>();
}

Еще одно преимуществоспециализация этого типа заключается в том, что вы обрабатываете различные типы матриц (пустые, не квадратные, 1x1, NxN) во время компиляции .Следовательно, вы получаете немного более быстрый код.

Кроме того, вы можете запретить вызов determinant для неквадратных матриц.В таком случае вы получите ошибку компилятора / компоновщика при попытке сделать это, а не исключение во время выполнения.

...