Динамически выделять неоднородную матрицу

Я пытаюсь создать общую функцию, которая будет динамически выделять 2D-структуру. Количество элементов в каждой строке не обязательно должно быть одинаковым для всех строк. Структура представлена ​​как тип контейнера, элементы которого также имеют тип контейнера (например, набор списков). Тип элементов этого внутреннего контейнера также может быть произвольным. Контейнеры поддерживают только функции начала, конца и размера. Операции итератора должны поддерживаться для всех типов итераторов. Функция должна сначала динамически выделить место для хранения 2D-структуры методом непрерывного выделения, а затем переписать принятые ею элементы структуры в динамическую структуру. Функция возвращает двойной указатель, по которому можно получить доступ к элементам этой структуры.

#include <iostream>
#include <set>
#include <list>
#include <vector>
template < typename tip >
  auto Make2DStructure(tip mat) {
    using tip_objekta = typename std::decay < decltype(mat[0][0]) > ::type;
    tip_objekta ** dynamic_mat = nullptr;
    int rows = 0, total = 0;
    for (auto i: mat) {
      rows++;
      for (auto j: i)
        total++;
    }
    int columns[rows];
    int k = 0;
    for (auto i: mat) {
      int num_of_colums = 0;
      for (auto j: i)
        num_of_colums++;
      columns[k] = num_of_colums;
      k++;
    }

    try {
      dynamic_mat = new tip_objekta * [rows];
      dynamic_mat[0] = new tip_objekta[total];
      for (int i = 1; i < rows; i++)
        dynamic_mat[i] = dynamic_mat[i - 1] + columns[i];
      for (int i = 0; i < rows; i++)
        for (int j = 0; j < columns[i]; j++)
          dynamic_mat[i][j] = mat[i][j];
    } catch (...) {
      delete[] dynamic_mat[0];
      delete[] dynamic_mat;
      throw std::bad_alloc();
    }
    return dynamic_mat;
  }

int main() {
    std::vector<std::vector<int>>mat{
        {1,2},
        {3,4,5,6},
        {7,8,9}
    };
    int columns[3] = {2,4,3};
  try {
    int ** dynamic_mat = Make2DStructure(mat);
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < columns[i]; j++)
        std::cout << dynamic_mat[i][j] << " ";
      std::cout << std::endl;
    }
    delete[] dynamic_mat[0];
    delete[] dynamic_mat;
  } catch (...) {
    std::cout << "Problems with memory";
  }
  return 0;
}

Как я могу изменить это, чтобы оно работало без индексации внутри Make2DStrucure()?

Кроме того, если бы я использовал std::set<std::list<int>> вместо std::vector<std::vector<int>> в основной функции, у меня были бы проблемы с дедукцией. Как я могу изменить это для работы с разными внешними и внутренними контейнерами?

Это звучит точно так же, как std::vector<std::vector<T>>. Вы пробовали это использовать?

Ted Lyngmo 03.05.2022 18:25

Это может быть очередь списков или вектор вектора, но дело в том, что я не знаю, какие типы контейнеров они будут. Функция является общей

Rocket Procd 03.05.2022 18:40

Хорошо, тогда, возможно, я неправильно понял. В чем причина возврата необработанного указатель-указатель вместо чего-то, что автоматически управляет временем жизни?

Ted Lyngmo 03.05.2022 18:50

это настройка задачи, используется двойной указатель из-за динамического распределения

Rocket Procd 03.05.2022 18:54

Делайте то, что требует от вас назначающий (если это для профессиональной работы, делайте все возможное, чтобы отговорить их от этого), но знайте, что передача необработанных указателей — это не то, как вы должны управлять динамическим выделением памяти в современном C++ (или любом другом C++). после стандартизации в 1998 г.).

user4581301 03.05.2022 18:58

спасибо, но вы заметили, что у меня есть некоторые проблемы внутри кода? Я согласен с тем, что двойной указатель не является хорошим выбором, но я не могу его скомпилировать из-за проблем типа вывода. Если бы я использовал вектор вектора в основной функции, у меня не было бы проблем с дедукцией, но в обоих случаях я не знаю, как считать строки и столбцы рваной матрицы.

Rocket Procd 03.05.2022 19:01

Я отредактировал код, теперь он работает для вектора вектора. Как я могу изменить это, чтобы он работал с набором списков или очередью списка или очередью векторов? типы контейнеров поддерживают только функции std::begin(), std::end() и size().

Rocket Procd 03.05.2022 19:12

Я не уверен, что именно вам нужно. Делает ли это это?

Ted Lyngmo 03.05.2022 19:20

это дает правильный результат, но я не знаю, использовали ли вы для этого постоянное распределение? Если да, не могли бы вы опубликовать это как ответ с некоторыми комментариями? Кроме того, я использую C++14, поэтому не могли бы вы изменить его для C++14?

Rocket Procd 03.05.2022 19:24

@Ted: посмотрите, можете ли вы использовать std::begin, чтобы вы также могли использовать необработанные массивы.

user4581301 03.05.2022 19:27

@ user4581301 Хорошая идея: godbolt.org/z/ejjjrWGjK

Ted Lyngmo 03.05.2022 19:29

@TedLyngmo, если это постоянное распределение, не могли бы вы опубликовать ответ на C++ 14 с некоторыми комментариями?

Rocket Procd 03.05.2022 19:32

@RocketProcd Хорошо, я опубликовал версию C++14 с комментариями.

Ted Lyngmo 03.05.2022 19:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
13
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот один из способов добиться того, что вы хотите:

#include <iterator>
#include <type_traits>

template <typename tip>
auto Make2DStructure(tip&& mat) {
    // create an alias for the value type:
    using value_type = std::decay_t<decltype(*std::begin(*std::begin(mat)))>;

    // allocate memory for the return value, the pointer-pointer:    
    value_type** rv = new value_type*[mat.size()]; // C++17: std::size(mat)

    // Calculate the number of values we need to allocate space for:
    size_t values = 0;
    for(auto& inner: mat) values += inner.size();  // C++17: std::size(inner)
    
    // allocate the space for the values:
    value_type* data = new value_type[values];

    // loop over the outer and inner container and keep the index running:   
    size_t idx = 0;

    for(auto& inner : mat) {
        // assign the outer pointer into the flat data block:
        rv[idx++] = data;
        for(auto& val : inner) {
            // assign values in the data block:
            *data++ = val;
        }
    }

    return rv;
}

С использованием std::size, где указано, это будет работать и с простыми массивами, а не только с классами контейнеров.

Во-первых, индексация внутри Make2DStructure() не должна поддерживаться, есть ли способ изменить это с помощью итераторов вместо индексации?

Rocket Procd 03.05.2022 19:43

@RocketProcd Прямо сейчас знания о размерах каждого «внутреннего» массива теряются при переводе в формат int**, но я думаю, что для этой конкретной настройки можно создать специальный тип итератора. Однако вам нужно будет хотя бы предоставить некоторую информацию итератору end для внешнего массива. Его нельзя вывести из информации, хранящейся в int**.

Ted Lyngmo 03.05.2022 19:53

есть ли способ уменьшить это std::remove_cv_t<std::remove_reference_t< decltype(*std::begin(*std::begin(std::declval<tip>())))>>; ?

Rocket Procd 03.05.2022 23:32

@RocketProcd Вы можете использовать фактический экземпляр вместо std::declval<tip>(), что сократит его до std::remove_cv_t<std::remove_reference_t< decltype(*std::begin(*std::begin(mat)))>>;. Вы также можете использовать функции-члены begin() вместо std::begin(), поскольку вам все равно требуется функция-член size(). Если бы вы могли использовать C++17, я бы использовал std::size(mat) вместо mat.size(), чтобы также иметь возможность отправлять простые массивы, но тогда мне нужно было бы добавить шаблон функции size, чтобы обеспечить ту же функциональность.

Ted Lyngmo 03.05.2022 23:36

спасибо, хоть немного меньше

Rocket Procd 03.05.2022 23:44

Я нашел другой способ using value_type = typename std::decay<decltype(*std::begin(*std::begin(mat)))>::type;

Rocket Procd 04.05.2022 16:07

@RocketProcd О, да, это на самом деле возвращается к std::remove_cv_t<std::remove_reference_t< ..., так что все будет хорошо. Вы даже можете использовать помощник std::decay_t в C++14, чтобы сделать его короче. Я обновил ответ.

Ted Lyngmo 04.05.2022 16:20

Другие вопросы по теме