基本的な考え方は次のようになります。
#include <vector>
#include <iostream>
template <class T>
class matrix {
size_t cols;
size_t rows;
std::vector<T> data;
class row_proxy { // This class is the part the question really asked about
size_t row;
matrix &m;
public:
row_proxy(matrix &m, size_t row) : row(row), m(m) {}
T &operator[](size_t col) {
if (row >= m.rows || col >= m.cols) // Note: row & col are indices not array count
throw std::logic_error("Bad index");
return m.data[row * m.cols + col];
}
};
public:
matrix(size_t cols, size_t rows) : rows(rows), cols(cols), data(rows*cols) {}
row_proxy operator[](size_t row) {
return row_proxy(*this, row);
}
};
int main() {
matrix<int> m(3, 3);
for (int i=0; i<3; i++) // fill the matrix with identifiable numbers
for (int j=0; j<3; j++)
m[i][j] = i * 100 + j;
for (int i=0; i<3; i++) { // show the content
for (int j=0; j<3; j++)
std::cout << m[i][j] << "\t";
std::cout << "\n";
}
try { // test the bounds checking.
m[4][1] = 21;
}
catch(std::logic_error &e) {
std::cerr << e.what();
}
return 0;
}
したがって、行列を作成するときは、そのサイズをrows
とに保存しcols
ます。マトリックスで使用する場合operator[]
、マトリックス内のアイテムへの参照を直接返そうとはしません。むしろ、行とマトリックスを追跡し、operator[]
その自分の。
したがって、を使用すると、最初のものはプロキシオブジェクトにmatrix[a][b]
保存a
されます。matrix
次に、[b]
パーツはそのプロキシオブジェクトで呼び出されます。a
これにより、との両方b
が行列用に保存した範囲内にあることが確認され、その場合は、ベクトル内の正しいオブジェクトへの参照が返されます。std::Logic_error
それ以外の場合は、 (最良の選択ではない可能性があります-私が最初に発生したものだけです)をスローします。
この一般的な考え方にはかなりのバリエーションがあることを付け加えておきます。ほんの一例として、コンパイル時に配列のサイズを指定できますが、サイズをテンプレートパラメータとして渡します。matrix<int, 2, 3>
これにはいくつかの利点があります。たとえば、matrix<int, 3, 2>
まったく異なるタイプであるため、誤って一方を他方に割り当てることはできません。また、いくつかの欠点もあります(最も明らかなのは、コンパイル時にサイズを知る必要があります。そうしないと、まったく機能しません)。