c++ - 递归模板函数内的无限循环

标签 c++ templates recursion matrix linear-algebra

我正在为大学项目编写自己的库,其中包含模板类:Vector和Matrix。除了这些模板类之外,还有用于 vector 和矩阵的相关模板函数。教授明确告诉我们将矩阵定义为一维数组,其中元素按列排序(效率/优化的原因)。 “矩阵”模板类具有3个模板参数:矩阵允许的数据类型,行数,列数。

template <class T, unsigned int M, unsigned int N>
class Matrix
话虽如此,我立即解决了这个问题。我正在编写一个函数,使用列的LaPlace规则(使用第一列)来计算任何维度> 4的矩阵的行列式。
我还编写了一个针对二维矩阵的函数(称为D2MatrixDet)和一个针对三维矩阵的函数(称为D3MatrixDet),这些函数已经过测试并可以正常工作:
template <class T>
double D2MatrixDet(const Matrix<T, 2, 2>& _m)

template <class T>
double D3MatrixDet(const Matrix<T, 3, 3>& _m)
我必须编写的模板函数具有两个模板参数:输入矩阵的数据类型,矩阵的维数(由于行列式是针对平方矩阵计算的,因此仅一个维就足够了)。它是一个递归函数;变量“结果”是在每个步骤中将行列式存储在内存中的变量。下面是我编写的代码。
template <class T, unsigned int D>
void DNMatrixDet(Matrix<T, D, D> _m, double result) //LaPlace Rule respect to the first column
{
    const unsigned int new_D = D - 1;
    Matrix<T, new_D, new_D> temp;

    if (D > 3)
    {
        for (unsigned int i = 0; i < _m.row; ++i)
        //Indicate the element to multiply
        {
            for (unsigned int j = _m.row, l = 0; j < _m.row * _m.column && l < pow(new_D, 2); ++j) 
            //Manage the element to be inserted in temp
            {
                bool invalid_row = false;

                for (unsigned int k = 1; k < _m.row && invalid_row == false; ++k) //Slide over row
                {
                    if (j == (i + k * _m.row))
                    {
                        invalid_row = true;
                    }
                }

                if (invalid_row == false)
                {
                    temp.components[l] = _m.components[j];
                    ++l;
                }
            }

            DNMatrixDet(temp, result);
            result += pow((-1), i) * _m.components[i] * result;
        }
    }
    else if (D == 3)
    {
        result += D3MatrixDet(_m);
    }
}
我主要使用5 x 5矩阵测试该功能。
当我尝试编译时,会出现一些错误,所有错误都非常相似,并且与矩阵的大小有关,矩阵的大小每步减小1。这是当初始矩阵大小为5时(LA是库的名称,而Test.cpp是包含主文件的文件):
LA.h: In instantiation of 'void LA::DNMatrixDet(LA::Matrix<T, M, M>, double) [with T = double; 
unsigned int D = 5]':
Test.cpp:437:33:   required from here
LA.h:668:34: error: no matching function for call to 'D3MatrixDet(LA::Matrix<double, 5, 5>&)'
             result += D3MatrixDet(_m);
                       ~~~~~~~~~~~^~~~
In file included from Test.cpp:1:
LA.h:619:12: note: candidate: 'template<class T> double LA::D3MatrixDet(const LA::Matrix<T, 3, 3>&)'
     double D3MatrixDet(const Matrix<T, 3, 3>& _m)
            ^~~~~~~~~~~
LA.h:619:12: note:   template argument deduction/substitution failed:
In file included from Test.cpp:1:
LA.h:668:34: note:   template argument '5' does not match '3'
             result += D3MatrixDet(_m);
                       ~~~~~~~~~~~^~~~
这是当大小变为4时:
LA.h: In instantiation of 'void LA::DNMatrixDet(LA::Matrix<T, M, M>, double) [with T = double; 
unsigned int D = 4]':
LA.h:662:28:   required from 'void LA::DNMatrixDet(LA::Matrix<T, M, M>, double) [with T = double; 
unsigned int D = 5]'
Test.cpp:437:33:   required from here
LA.h:668:34: error: no matching function for call to 'D3MatrixDet(LA::Matrix<double, 4, 4>&)'
In file included from Test.cpp:1:
LA.h:619:12: note: candidate: 'template<class T> double LA::D3MatrixDet(const LA::Matrix<T, 3, 3>&)'
     double D3MatrixDet(const Matrix<T, 3, 3>& _m)
            ^~~~~~~~~~~
LA.h:619:12: note:   template argument deduction/substitution failed:
In file included from Test.cpp:1:
LA.h:668:34: note:   template argument '4' does not match '3'
             result += D3MatrixDet(_m);
                       ~~~~~~~~~~~^~~~
等等。它一直下降直到从4294967295(我发现这是32位“unsigned int”的上限)开始重新开始,一直下降直到达到模板实例的最大数量(= 900)为止。
在每次迭代中,即使仅在输入矩阵为3 x 3时才执行该函数,编译器始终会检查该函数以计算3 x 3的行列式。因此,为什么要检查理论上永远不应该进行的检查即将发生?
即使在纸上写的矩阵的帮助下,我也仔细检查了几次编写的数学逻辑,并缓慢地执行了第一步。我相信并希望它是对的。我很确定问题与使用模板和递归函数有关。
对于很长的问题,我深表歉意,我试图以最好的方式进行解释。我希望我已经很好地解释了这个问题。
编辑:
通过在DNMatrixDet函数的开头定义“if constexpr”来解决问题。编译成功。我只需要修复算法,但这超出了本文的范围。下面是进行更改的reprex:
template <class T, unsigned int M, unsigned int N>
class Matrix
{
    public:

    T components[M * N];
    unsigned int row = M;
    unsigned int column = N;

    Matrix()
    {
        for (unsigned int i = 0; i < M * N; ++i)
        {
            components[i] = 1;
        }
    }
    Matrix(T* _c)
    {
        for (unsigned int i = 0; i < M * N; ++i, ++_c)
        {
            components[i] = *_c;
        }
    }
    friend std::ostream& operator<<(std::ostream& output, const Matrix& _m)
    {
        output << _m.row << " x " << _m.column << " matrix:" << std::endl;

        for (unsigned int i = 0; i < _m.row; ++i)
        {
            for (unsigned int j = 0; j < _m.column; ++j)
            {
                if (j == _m.column -1)
                {
                    output << _m.components[i + j*_m.row];
                }
                else
                {
                    output << _m.components[i + j*_m.row] << "\t";
                }
            }

            output << std::endl;
        }

        return output;
    }
};
template <class T>
double D3MatrixDet(const Matrix<T, 3, 3>& _m)
{
    double result = _m.components[0] * _m.components[4] * _m.components[8] + 
                    _m.components[3] * _m.components[7] * _m.components[2] +
                    _m.components[6] * _m.components[1] * _m.components[5] -
                    (_m.components[6] * _m.components[4] * _m.components[2] +
                     _m.components[3] * _m.components[1] * _m.components[8] +
                     _m.components[0] * _m.components[7] * _m.components[5]);

    return result;
}
template <class T, unsigned int D>
void DNMatrixDet(Matrix<T, D, D> _m, double result)
{
    Matrix<T, D - 1, D - 1> temp;

    if constexpr (D > 3)
    {
        for (unsigned int i = 0; i < D; ++i)
        {
            for (unsigned int j = D, l = 0; j < D * D && l < (D - 1) * (D - 1); ++j)
            {
                bool invalid_row = false;

                for (unsigned int k = 1; k < D && invalid_row == false; ++k)
                {
                    if (j == (i + k * D))
                    {
                        invalid_row = true;
                    }
                }

                if (invalid_row == false)
                {
                    temp.components[l] = _m.components[j];
                    ++l;
                }
            }

            DNMatrixDet(temp, result);
            result += i & 1 ? -1 : 1 * _m.components[i] * result;
        }
    }
    else if (D == 3)
    {
        result += D3MatrixDet(_m);
    }
}

int main()
{
    double m_start[25] = {4, 9, 3, 20, 7, 10, 9, 50, 81, 7, 20, 1, 36, 98, 4, 20, 1, 8, 5, 93, 47, 21, 49, 36, 92};
    Matrix<double, 5, 5> m = Matrix<double, 5, 5> (m_start);
    double m_det = 0;
    DNMatrixDet(m, m_det);
    std::cout << "m is " << m << std::endl;
    std::cout << "Det of m is " << m_det << std::endl;

    return 0;
}

最佳答案

当您以类型_m作为参数Matrix<T, 5, 5>传递时,结尾的else分支包含代码result += D3MatrixDet(_m);。编译器仍将尝试对此进行编译,并注意找不到匹配的构造函数。
由于我们在编译时就知道是否采用该分支,因此可以通过使用if constexpr来指示编译器。由于我们位于模板内,因此如果该分支被丢弃,编译器将不再对其进行检查。
因此,让我们将if (D > 3)更改为if constexpr (D > 3)

关于c++ - 递归模板函数内的无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64136273/

相关文章:

java - 计算代表 n 分的可能组合的数量

java - 为什么如果我直接添加答案,我的列表函数在这个问题中不起作用,但如果我创建列表的副本则起作用

c++ - 将 Excel 数据加载到 Linux/wxWidgets C++ 应用程序中?

android - #define 预处理器如何在 C 中真正工作

c++ - 关于::(scope_resolution_operator) 用法的疑问

templates - 专有代码的代码 header

c++ - 了解模板类型/值的重复评估

c++ - char * 是什么意思

c++ - 请求从 lambda 到非标量类型的转换

javascript - 为什么我的数组在 Javascript 中将自身分开?