[英]C++ recursive function with template results in zero-sized array
我在矩陣類中實現了一個行列式函數,我想通過遞歸地得到一個越來越小的次矩陣來計算它。 問題在於編譯器最終創建了一個維度為 0 的矩陣,即使我有一個 if 語句來確保這不會在運行時發生。
關於這個我有兩個問題:
這是標題,刪除了所有不相關的內容。 完整源代碼在這里: https : //github.com/DanielEverland/Artemis/blob/master/ArtemisEngineCore/Engine/Math/Matrices/GenericMatrix.h
初始矩陣是 double 類型的 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
正如評論中已經指出的那樣,您必須通過定義專業化 GenericMatrix 來停止遞歸。 if(false)
在運行時進行評估(如評論中已經指出的 c++17 特性if consexpr
也可以在此處使用,但是以下答案並非基於該特性,因為該問題未標記為 c+ +17)。 這直接回答了你的第一個問題。 請參閱以下示例:
GenericMatrix<double,3,3> mat;
auto det = mat.GetDeterminant();
mat.GetDeterminant()
內部調用GetMinor
返回GenericMatrix<double,2,2>
。 現在返回的GenericMatrix<double,2,2>
GetDeterminant
本身調用GetDeterminant
並且一切從頭開始,因為if(false)
不會停止編譯時遞歸。 因此,您必須提供一個特殊化template<class T> GenericMatrix<T,1,1>
其GetDeterminant
不會調用GetMinor
關於第二個問題,這里有一個簡化的例子:
#include <iostream>
template<class T, unsigned int rows, unsigned int columns>
class GenericMatrix
{
public:
// Returns the determinant of the matrix
// Requires the matrix to be square
T GetDeterminant() const
{
T determinant = 0.;
std::cout << "call GenericMatrix<T," << rows <<","
<< columns <<">::GetDeterminant" << std::endl;
auto det_minor = this->GetMinor(0, 0).GetDeterminant();
// do semething with det_minor
return determinant;
}
private:
// 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
{
GenericMatrix<T, rows - 1, columns - 1> minor{};
std::cout << "call GenericMatrix<T,"
<< rows <<","<< columns <<">::GetMinor with return type "
<< "GenericMatrix<T," << rows-1 <<","
<< columns-1 <<">::GetDeterminant"
<< std::endl;
return minor;
}
T values[rows][columns] = { };
};
template<class T>
class GenericMatrix<T,1,1>
{
public:
// Returns the determinant of the matrix
T GetDeterminant() const
{
T determinant = 0.;
std::cout << "call GenericMatrix<T,1,1>::GetDeterminant" << std::endl;
return determinant;
}
private:
T value = { };
};
int main()
{
GenericMatrix<double,4,4> mat;
std::cout << mat.GetDeterminant() << std::endl;
return 0;
}
這是輸出的編譯代碼
call GenericMatrix<T,4,4>::GetDeterminant
call GenericMatrix<T,4,4>::GetMinor with return type GenericMatrix<T,3,3>::GetDeterminant
call GenericMatrix<T,3,3>::GetDeterminant
call GenericMatrix<T,3,3>::GetMinor with return type GenericMatrix<T,2,2>::GetDeterminant
call GenericMatrix<T,2,2>::GetDeterminant
call GenericMatrix<T,2,2>::GetMinor with return type GenericMatrix<T,1,1>::GetDeterminant
call GenericMatrix<T,1,1>::GetDeterminant
0
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.