Смотреть в Telegram
​​Допотопный доступ к многомерному массиву Ч2 Теперь пойдут способы доступов к элементам многомерных массивов не каноничным путем. Самый простой из них - вместо оператора[], который может принимать только один параметр, использовать оператор(), который можно перегружать, как нашей душе угодно. Он может принимать ничем не ограниченный набор параметров. И к тому же их несколько штук можно определить. Например, если передаем 1 аргумент, то возвращается ссылка на строку матрицы. А если 2 аргумента, то возвращаем ссылку на сам элемент. Вот так это может выглядеть:
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};
  }
  
  T& operator()(std::size_t row, std::size_t col) {
    return data[row * COLS + col];
  }
  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;
  }
}
Идейно здесь все тоже самое, да и пример почти идентичный, только используем оператор() вместо квадратных скобок. Это нам позволило в одном классе определить, как мы хотим давать доступы по одному и двум индексам. Это также упростило компилятору задачу по инлайнингу доступа к элементу по двум индексам, так это один вызов функции. Но у метода есть и недостатки. Самый очевидный - все привыкли использовать квадратные скобки для индексов, а теперь только для этих классов нужно использовать круглые. При чтении кода иногда сложно отличить одно от другого и будут ошибки. Конечно, в момент компиляции все встанет на свои места, но все равно неприятно. Но если вы не сами пишите все это добро, а используете какой-то мощный алгебраический фреймворк, типа Eigen, то вам нативные сущности могут вообще не понадобится. Фреймворк будет покрывать все потребности и код станет консистентным. Из неочевидного: для типа строки тоже придется использовать круглые скобки, чтобы сохранить сходство обработки с матрицами(на примере во втором цикле для доступа к конкретному элементу прокси массива тоже используем ()). Ну или не придется, но тогда сходства работы не будет и возникнет еще сильнее путаница. Ну и непонятно, мы вообще функцию вызываем или что?? Тем не менее, этот способ есть даже в FAQ на странице, посвященному стандарту C++. И он совместим с нотацией индексации в Фортране. Поэтому метод довольно распространенный. Be consistent. Stay cool. #cppcore
Love Center - Dating, Friends & Matches, NY, LA, Dubai, Global
Love Center - Dating, Friends & Matches, NY, LA, Dubai, Global
Бот для знакомств