私が知っているほとんどの実装では、2D データにアクセスするために MACROS またはメソッドのいずれかを介して、1D 配列とラッパーを使用しています。私自身、どちらが速いかは言えませんし、これが本当に問題になるケースもほとんど見当たりません。
ただし、次の理由から 1D 配列を使用することをお勧めします。
- 動的に割り当てて解放するのが簡単です (データを動的に割り当てて、matrix[i][j] パターンを維持したい場合は、配列の配列を割り当てます - 割り当てが遅くなり、バグが発生しやすく、各配列を個別に解放する必要があります)
- 動的に割り当てて、データをOpenGLやOpenCVなどのサードパーティライブラリにコピーする必要がある場合、データが連続している必要があるため、問題が発生します。
- ファイルまたはその他の永続的なストレージからマトリックスを保存および読み取る方が簡単です。
- 動的に割り当てなくても、3d パーティ ライブラリへのメモリの転送を処理するために、あらゆる種類の醜いキャストを行う必要があります。
- 乗算や不明確なインデックスを使用せずに、データへの効率的な 2 次元アクセスを行うメソッドを使用して構造を作成するのは非常に簡単です。または、MACROS を使用して醜い乗算をカプセル化します。
- 次のサンプルのように見える構造を作成して、2 次元配列の読みやすさで 1 次元配列と同じ利点を維持することもできます。
template<typename T>
struct Matrix4x4
{
struct index
{
int row, col;
index(int r = 0, int c = 0)
: row(r), col(c){}
index(index const & cp)
: row(cp.row), col(cp.col)
{
}
//Assignment ommited for brevity
};
/*
Constructors, Assignments etc. ommited for brevity
*/
T m00, m01, m02, m03;
T m10, m11, m12, m13;
T m20, m21, m22, m23;
T m30, m31, m32, m33;
T * toArray() const
{
return &m00;
}
T * toArray()
{
return &m00;
}
T * row(int r)
{
return (&m00) + r*4;
}
T * row(int r) const
{
return (&m00) + r*4;
}
T & operator()(int r, int c)
{
return *row(r)[c];
}
T const & operator()(int r, int c) const
{
return *row(r)[c];
}
T & operator[](index const & idx)
{
return row(idx.row)[idx.col];
}
T const & operator[](index const & idx) const
{
return row(idx.row)[idx.col];
}
};
あなたのコードでは、次のことができます。
typedef Matrix4x4<double> Matrix4x4d;
Matrix4x4d mat;
/* You can do any of the following */
mat.m23 = 6.0;
mat(2,3) = 6.0;
mat[Matrix4x4d::index(2,3)] = 6.0;
mat.row(2)[3] = 6.0;
mat.toArray()[2*4 + 3] = 6.0;
#define M(m,r,c) (*((&m.m00) + r*4 + c))
M(mat,2,3) = 6.0;
私自身、何年にもわたっていくつかの行列ライブラリを実装し、常に 1d ソリューションを選択しました。