【发布时间】:2020-01-27 12:10:05
【问题描述】:
我正在一个矩阵类中实现一个行列式函数,我想通过递归地得到一个越来越小的次要矩阵来计算它。问题在于编译器最终会创建一个维度为 0 的矩阵,即使我有一个 if 语句可以确保在运行时不会发生这种情况。
对此我有两个问题:
- 为什么编译器会实例化在运行时无法实例化的特化? - 我觉得我错过了一些非常明显的东西。
- 如何更改代码以修复递归?
这是删除所有无关内容的标题。完整源代码在这里:https://github.com/DanielEverland/Artemis/blob/master/ArtemisEngineCore/Engine/Math/Matrices/GenericMatrix.h
初始矩阵是双精度类型的 4x4 矩阵。
template<class T, unsigned int rows, unsigned int columns>
class GenericMatrix : BaseMatrix
{
public:
// Returns the determinant of the matrix
// Requires the matrix to be square
T GetDeterminant() const
{
static_assert(rows == columns, "Cannot get determinant of non-square matrix");
T determinant{};
// Seeing as this is always false, a minor matrix is never created, and a zero-sized array should never be created.
if(false)
GetMinor(0, 0).GetDeterminant(); // If I comment-out this line the build succeeds
return determinant;
}
// Returns the minor of this matrix.
// Requires the matrix to be square.
// Will return a matrix[N - 1, N - 1] with a removed row and column.
GenericMatrix<T, rows - 1, columns - 1> GetMinor(unsigned int rowToDelete, unsigned int columnToDelete) const
{
static_assert(rows == columns, "Cannot get minor of non-square matrix");
static_assert(rows >= 2, "Cannot get minor of a matrix that has 2 rows or fewer.");
static_assert(columns >= 2, "Cannot get minor of a matrix that has 2 column or fewer.");
GenericMatrix<T, rows - 1, columns - 1> minor{};
unsigned int rowIndex = 0;
for (unsigned int i = 0; i < minor.GetRows(); i++)
{
if (rowIndex == rowToDelete)
rowIndex++;
unsigned int columnIndex = 0;
for (unsigned int j = 0; j < minor.GetColumns(); j++)
{
if (columnIndex == columnToDelete)
columnIndex++;
minor[i][j] = values[rowIndex][columnIndex];
columnIndex++;
}
rowIndex++;
}
return minor;
}
private:
T values[rows][columns] = { };
};
这是构建输出。如您所知,实例化了一个维度为 0 的矩阵
1>------ Build started: Project: UnitTests, Configuration: Debug x64 ------
1>MatrixTests.cpp
1>C:\Users\Daniel\source\repos\Artemis\ArtemisEngineCore\Engine\Math\Matrices\GenericMatrix.h(234,1): error C2087: 'values': missing subscript
1>C:\Users\Daniel\source\repos\Artemis\ArtemisEngineCore\Engine\Math\Matrices\GenericMatrix.h(200): message : see reference to class template instantiation 'ArtemisEngine::Math::Matrices::GenericMatrix<T,0,0>' being compiled
1> with
1> [
1> T=Math::Matrices::T
1> ]
1>C:\Users\Daniel\source\repos\Artemis\ArtemisEngineCore\Engine\Math\Matrices\GenericMatrix.h(194): message : while compiling class template member function 'T ArtemisEngine::Math::Matrices::GenericMatrix<T,1,1>::GetDeterminant(void) const'
1> with
1> [
1> T=Math::Matrices::T
1> ]
1>C:\Users\Daniel\source\repos\Artemis\ArtemisEngineCore\Engine\Math\Matrices\GenericMatrix.h(200): message : see reference to function template instantiation 'T ArtemisEngine::Math::Matrices::GenericMatrix<T,1,1>::GetDeterminant(void) const' being compiled
1> with
1> [
1> T=Math::Matrices::T
1> ]
1>C:\Users\Daniel\source\repos\Artemis\UnitTests\Math\MatrixTests.cpp(375): message : see reference to class template instantiation 'ArtemisEngine::Math::Matrices::GenericMatrix<Math::Matrices::T,1,1>' being compiled
1>C:\Users\Daniel\source\repos\Artemis\UnitTests\Math\MatrixTests.cpp(57): message : see reference to class template instantiation 'ArtemisEngine::Math::Matrices::GenericMatrix<Math::Matrices::T,4,4>' being compiled
1>C:\Users\Daniel\source\repos\Artemis\ArtemisEngineCore\Engine\Math\Matrices\GenericMatrix.h(234,1): warning C4200: nonstandard extension used: zero-sized array in struct/union
1>C:\Users\Daniel\source\repos\Artemis\ArtemisEngineCore\Engine\Math\Matrices\GenericMatrix.h(234,1): message : This member will be ignored by a defaulted constructor or copy/move assignment operator
1>Done building project "UnitTests.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 2 up-to-date, 0 skipped ==========
Build time 00:00:00.726
Build ended at 27/01/2020 12.56.52
【问题讨论】:
-
与递归一样,您需要一个在某处停止递归的基本情况。顺便说一句,“在运行时无法实例化的特化”......不,这一切都发生在编译时
-
由于 if(false),在我粘贴的代码中永远不会发生递归。我理解其中的区别,我的意思是我不明白为什么要实例化它,因为它永远不能在运行时使用。
-
if(false)在运行时评估,但在编译时所有分支都必须正确。constexpr if可能会有所帮助 -
它不仅仅是
if(false),一种方法返回GenericMatrix<T, rows - 1, columns - 1> -
可以,但只在 if 语句中调用。
if constexpr(false)有效,但实际转义条件if constexpr(rows >= 2 && columns >= 2)无效。