Возвращать указатель или значение, когда объекты большие - PullRequest
1 голос
/ 27 апреля 2020

Скажем, мы создаем класс Matrix<n, m>, который хранит целые числа nxm в переменной-члене std::array<std::array<int, m>, n> inner;. Теперь есть два способа сделать сложение:

Метод 1 ) Возврат по значению (возможно constexpr)

template<int n, int m> class Matrix {
    ...
    constexpr Matrix add(const Matrix& that) const {
        matrix ret;
        for (int y = 0; y < n; y++)
            for (int x = 0; x < m; x++)
                ret.inner[y][x] = this->inner[y][x] + that.inner[y][x];
        return ret;
    }
    ...
}

Метод 2 ) Возврат указателя (constexpr невозможен)

template<int n, int m> class Matrix {
    ...
    Matrix *add(const Matrix& that) const {
        Matrix *ret = new Matrix();
        for (int y = 0; y < n; y++)
            for (int x = 0; x < m; x++)
                ret->inner[y][x] = this->inner[y][x] + that.inner[y][x];
        return ret;
    }
    ...
}

Моя программа должна выполнить арифметику c с 1000x1000 матрицами (изображениями), поэтому используется Метод 1 Я получаю переполнение стека немедленно. Моя программа также работает с небольшими 4x4 матрицами (матрицами плотности в квантовых вычислениях), которые необходимо вычислять во время компиляции, поэтому использование Method 2 невозможно при этих constexpr инициализациях.

Вопрос : мне нужно сделать две версии каждого метода, возвращающего Matrix? (один возвращает Matrix, а другой возвращает Matrix*) Это будет много повторяющегося кода. Насколько распространена эта проблема? Есть ли альтернативный способ использования кучи, так что Метод 2 также возможен из constexpr?

В этом ответе упоминается, что семантика перемещения немного сместила предпочтение в Метод 1 . Но как насчет переполнения стека?

1 Ответ

1 голос
/ 27 апреля 2020

Мое предложение:

template<size_t n, size_t m> class Matrix
{
public:
    Matrix& operator+=(const Matrix& other)
    {
        return *this;
    }
};

template<size_t n, size_t m>
Matrix<n, m> constexpr operator+(Matrix<n, m> x, Matrix<n, m> const& y)
{
    x += y;
    return x;
}

template<size_t n, size_t m>
std::unique_ptr<Matrix<n, m>> add(Matrix<n, m> const* x, Matrix<n, m> const* y)
{
    auto result = std::make_unique<n, m>(*x);
    *result += *y;
    return std::unique_result;
}

Теперь вы можете использовать сложение по значению для маленьких матриц:

Matrix<10, 12> m1, m2;
auto m3 = m1 + m2;

и больших матриц, которые вы можете динамически распределять:

auto pm1 = std::make_unique<Matrix<12, 10>>();
auto pm2 = std::make_unique<Matrix<12, 10>>();
auto pm3 = add(pm1.get(), pm2.get());

Или даже:

auto pm3 = std::make_unique<Matrix<12, 10>>(pm1);
*pm3 += *pm2;
...