TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #563 5.64K
​​Допотопный доступ к многомерному массиву Ч1
#опытным

Начнем рассказ о том, как люди до С++23(то есть до сих пор) жили с оператором[], принимающим только один параметр.

И начнем мы с банальщины. Вот у нас есть класс матрицы. По всем канонам С++ мы должны получать доступ к ее элементам вот так matrix[i][j]. Этот формат сохраняет констистентность с доступом к элементам одномерных массивов.

Однако сразу натыкаемся на проблему. Класс один, а вызываем мы оператор два раза. Несостыковочка.

Ее решает паттерн прокси. Мы создаем прокси класс и возвращаем его объект из первого индекса. Дальше у этого прокси класса определяем оператор[] и на выходе получаем наш элемент.

Условно, из первого оператора возвращаем ссылку на строку матрицы, а из второго - уже сам элемент.

Выглядит это примерно так:

template <typename T>
struct ArraySpan {
ArraySpan(T * arr, size_t arr_size) : data_{arr}, size_{arr_size} {}
ArraySpan(T * arr_begin, T * arr_end)
: data_{arr_begin}
, size_{std::distance(arr_begin, arr_end)} {}

T& operator[](std::size_t i) {
return *(data_ + i);
}

size_t size() const {return size_;}
T * data() {return data_;}
private:
T * data_;
size_t size_;
};

template <typename T>
struct Matrix {
Matrix() = default;
Matrix(size_t rows, size_t cols, T init) : ROWS{rows}, COLS{cols}, data(ROWS * COLS, init) {}
Matrix(Matrix const&) = default;
ArraySpan<T> operator[](std::size_t row) {
return ArraySpan{data.data() + row * COLS, COLS};
}
std::vector<T>& underlying_array() { return data; }
size_t row_count() const { return ROWS;}
size_t col_count() const { return COLS;}
private:
size_t ROWS;
size_t COLS;
std::vector<T> data;
};

int main() {
Matrix mtrx(4, 5, 0);
auto& interval_buffer = mtrx.underlying_array();
std::iota(interval_buffer.begin(), interval_buffer.end(), 0);
for (int i = 0; i < mtrx.row_count(); ++i) {
for (int j = 0; j < mtrx.col_count(); ++j) {
std::cout << std::setw(2) << mtrx[i][j] << " ";
}
std::cout << std::endl;
}
std::cout << std::endl;
for (int i = 0; i < mtrx.row_count(); ++i) {
auto row = mtrx[i];
for (int j = 0; j < row.size(); ++j) {
std::cout << std::setw(2) << row[j] << " ";
}
std::cout << std::endl;
}
}

В примере мы 2 раза проходимся по матрице, чтобы продемонстрировать возможность индексации элементов через объект самой матрицы и объекта строки.

Можно было бы конечно не писать отдельно наш прокси тип ArraySpan, а использовать готовый std::span из С++20, но оставим так. Идея использовать такой легковесный объект понятна - нам не нужно лишнего оверхеда на копирование или создание сложного объекта, чтобы просто получить доступ к элементу матрицы.

Ну и здесь мы можем использовать прокси тип в качестве реальной строки матрицы, и не скрывать его в кишках класса. Так мы получаем доступ к большей вариативности в оперировании матрицами. Например, можно делать скалярное произведение строк и прочее.

В чем недостаток такого способа? Куча прокси классов, которые возможно и не нужны. Возможно, нам просто нужна двумерная структура, чтобы получать доступ к конкретным элементам. Использование карты для морского боя не предполагает использование отдельных строк или столбцов. Хотелось бы просто индексировать конкретные элементы. Но тем не менее мы вынуждены использовать прокси класс.

А если структура трухмерная? Уже 2 прокси нужно будет. Больше вложенность - больше классов-прослоек. Не очень удобно. Поэтому и придумали другие способы, о которых расскажу в следующих постах.

Find the way out. Stay cool.

#cppcore #cpp20 #cpp23
  • ❤ 18
  • 👍 12
  • 🔥 7
  • ❤‍🔥 2
  • 😁 2
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →