在运行时更改数组尺寸 [英] Change array dimensions at runtime

查看:71
本文介绍了在运行时更改数组尺寸的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个具有二维双精度数组的矩阵类.在构造函数中,您可以指定宽度和高度.当宽度为1时,我想创建一个1d数组而不是2d数组.因为我重载了[]运算符并返回了指针.如果只有1行/列,我不想总是写[i] [0].相反,我只想写[i]. 有人知道如何解决这个问题吗?

I have this matrix class which has a 2d double array. In the constructor you can specify the width and height. I want to create a 1d array instead of a 2d when the width is 1. Because I overloaded the [] operator and return the pointer. If there is only 1 row/col I don't want to always write [i][0]. Instead I want to just write [i]. Does anyone know how to solve this?

为了澄清这一点,我需要这个类用于矩阵计算,而不仅仅是数组.

To clarify this I need this class for matrix calculations no just as array.

推荐答案

我建议使用一维数组,因为建议 user2079303在他们的答案中.但是,如果不希望这样做,也可以使用模板或多态性来实现您的想法.

I would recommend using a one-dimensional array, as user2079303 suggested in their answer. If this isn't desirable, however, it's also possible to implement your idea with either templates or polymorphism.

如果不需要即时更改其尺寸,则可以将其尺寸用作模板参数,使矩阵成为模板化类.反过来,这允许将尺寸用于SFINAE的编译时逻辑,这使您可以根据矩阵的尺寸重载operator[]().

If you don't need to be able to change its dimensions on the fly, you could make your matrix a templated class, using its dimensions as template parameters. This, in turn, allows the dimensions to be used for compile-time logic with SFINAE, which allows you to overload operator[]() based on the matrix's dimensions.

请注意,当Rows == 1Cols == 1时,这实际上并不是 创建一维数组,它只是模拟一个数组.如果您明确需要该行为,而不是在技术上有所不同,但大多数情况下是等效的行为,则需要研究对一个或两个参数为1的情况进行专门化的类.这可能会涉及到至少复制一些代码,因此会有些混乱.

Note that this doesn't actually create a 1D array when Rows == 1 or Cols == 1, it merely simulates one; if you explicitly need that behaviour, instead of technically different but mostly equivalent behaviour, you'll want to look into specialising the class for when one or both parameters is 1. This will likely involve duplicating at least some of the code, so it'll be a bit messy.

// This example uses std::array for the actual array, since it's compile-time anyways.
// This, in turn, lets it declare a lot of functions constexpr.
// Const-correctness omitted for brevity.  Remember to restore it for actual code.
template<size_t Rows, size_t Cols>
class Matrix {
    std::array<std::array<double, Cols>, Rows> mat;

  public:
    // Default constructor. Clang _really_ likes braced initialiser lists.
    constexpr Matrix() : mat{{{{0}}}} {}

    // Array constructor.
    constexpr Matrix(const decltype(mat)& arr) : mat(arr) {}

    // -----

    // Subscript operators.

    // Generic operator.  Matrix<x, y>, where x != 1 && y != 1.
    // Does what normal subscript operators do.
    template<bool R = (Rows == 1), bool C = (Cols == 1)>
    auto& operator[](std::enable_if_t<!R && !C, size_t> i) {
        return mat[i];
    }

    // Magic operator.  Matrix<1, x>, where x != 1.
    template<bool R = (Rows == 1), bool C = (Cols == 1)>
    auto& operator[](std::enable_if_t<(R && !C), size_t> i) {
        return mat[0][i];
    }

    // Magic operator.  Matrix<x, 1>, where x != 1.
    template<bool R = (Rows == 1), bool C = (Cols == 1)>
    auto& operator[](std::enable_if_t<C && !R, size_t> i) {
        return mat[i][0];
    }

    // Scalar matrix operator.  Matrix<1, 1>.
    // Just returns mat[0][0], for simplicity's sake.  Might want to make it do something
    //  more complex in your actual class.
    template<bool R = (Rows == 1), bool C = (Cols == 1)>
    auto& operator[](std::enable_if_t<R && C, size_t> i) {
        return mat[0][0];
    }

    // -----

    // A few interface helpers.

    // Simple begin() & end(), for example's sake.
    // A better version would begin at mat[0][0] and end at mat[Rows - 1][Cols - 1].
    constexpr auto begin() const { return mat.begin(); }
    constexpr auto   end() const { return mat.end(); }

    // Generic helpers.
    constexpr size_t    size() const { return mat.size() * mat[0].size(); }
    constexpr size_t    rows() const { return mat.size(); }
    constexpr size_t    cols() const { return mat[0].size(); }

    // 1D Matrix helpers.
    constexpr bool    is_one_d() const { return (Rows == 1) || (Cols == 1); }
    constexpr bool     one_row() const { return Rows == 1; }
    constexpr size_t dimension() const { return (one_row() ? cols() : rows()); }

    // -----

    // Output.
    // Would need modification if better begin() & end() are implemented.
    friend std::ostream& operator<<(std::ostream& str, const Matrix<Rows, Cols>& m) {
        for (auto& row : m) {
            for (auto& elem : row) {
                str << std::setw(6) << elem << ' ';
            }
            str << '\n';
        }
        str << std::endl;
        return str;
    }
};

它可以用作...

// Get rid of any "Waah, you didn't use that!" warnings.
// See https://stackoverflow.com/a/31654792/5386374
#define UNUSED(x) [&x]{}()

// This should really use if constexpr, but online compilers don't really support it yet.
// Instead, have an SFINAE dummy.
template<size_t Rows, size_t Cols>
void fill(Matrix<Rows, Cols>& m, std::enable_if_t<(Rows == 1) || (Cols == 1), int> dummy = 0) {
    UNUSED(dummy);

    //for (size_t i = 0; i < (m.one_row() ? m.cols() : m.rows()); i++) {
    for (size_t i = 0; i < m.dimension(); i++) {
        m[i] = (i ? i : 0.5) * (i ? i : 0.5);
    }
}

template<size_t Rows, size_t Cols>
void fill(Matrix<Rows, Cols>& m, std::enable_if_t<!((Rows == 1) || (Cols == 1)), int> dummy = 0) {
    UNUSED(dummy);

    for (size_t i = 0; i < m.rows(); i++) {
        for (size_t j = 0; j < m.cols(); j++) {
            m[i][j] = (i ? i : 0.5) * (j ? j : 0.5) + (i >= j ? 0.1 : -0.2);
        }
    }
}

请在此处查看.

如果要做需要能够即时更改其尺寸,这将变得更加复杂.最简单的解决方案可能是使用多态性,并让operator[]返回代理.

If you do need to be able to change its dimensions on the fly, this becomes more complex. The easiest solution would probably be to use polymorphism, and have operator[] return a proxy.

class Matrix {
  protected:
    // Out proxy class.
    // Visible to children, for implementing.
    struct SubscriptProxy {
        virtual operator double&() = 0;
        virtual operator double*() = 0;
        virtual double& operator=(double) = 0;
        virtual double& operator[](size_t) = 0;
        virtual ~SubscriptProxy() = default;
    };

  public:
    virtual SubscriptProxy& operator[](size_t i) = 0;
    virtual ~Matrix() = default;

    virtual void out(std::ostream& str) const = 0;
    friend std::ostream& operator<<(std::ostream& str, const Matrix& m) {
        m.out(str);
        return str;
    }
};
std::ostream& operator<<(std::ostream& str, const Matrix& m);

然后,您可以让每个类将需要的代理成员设为public,将不需要的成员作为privateprivate的代码会获得非功能性的虚拟实现.

You can then have each class make the proxy members it needs public, and the ones it doesn't private; the private ones get a non-functional dummy implementation.

// Resizing omitted for brevity.
class OneDMatrix : public Matrix {
    double arr[5];

    // Proxy for single element.
    class OneDProxy : public SubscriptProxy {
        double& elem;

        operator double*() override { return &elem; }
        double& operator[](size_t) override { return elem; }
      public:
        OneDProxy(double& e) : elem(e) {}

        operator double&() override { return elem; }
        double& operator=(double d) override {
            elem = d;
            return elem;
        }
    };

  public:
    OneDMatrix() : arr{0} {}

    // operator[] maintains a static pointer, to keep the return value alive and guarantee
    //  proper cleanup.
    SubscriptProxy& operator[](size_t i) override {
        static OneDProxy* ret = nullptr;

        if (ret) { delete ret; }
        ret = new OneDProxy(arr[i]);
        return *ret;
    }

    void out(std::ostream& str) const override {
        for (size_t i = 0; i < 5; i++) {
            str << std::setw(4) << arr[i] << ' ';
        }
        str << std::endl;
    }
};

// Resizing omitted for brevity.
class TwoDMatrix : public Matrix {
    double arr[3][4];

    // Proxy for array.
    class TwoDProxy : public SubscriptProxy {
        double* elem;

        operator double&() override { return elem[0]; }
        double& operator=(double) override { return elem[0]; }
      public:
        TwoDProxy(double* e) : elem(e) {}
        operator double*() override { return elem; }
        double& operator[](size_t i) override { return elem[i]; }
    };

  public:
    TwoDMatrix() : arr{{0}} {}

    // operator[] maintains a static pointer, to keep the return value alive and guarantee
    //  proper cleanup.
    SubscriptProxy& operator[](size_t i) override {
        static TwoDProxy* ret = nullptr;

        if (ret) { delete ret; }
        ret = new TwoDProxy(arr[i]);
        return *ret;
    }

    void out(std::ostream& str) const override {
        for (size_t i = 0; i < 3; i++) {
            for (size_t j = 0; j < 4; j++) {
                str << std::setw(4) << arr[i][j] << ' ';
            }
            str << '\n';
        }
    }
};

由于引用可用于多态性,因此Matrix可以用作接口,从而允许不需要特别要求实际类之一的任何对象使用Matrix&.

Due to references being usable for polymorphism, Matrix can be used as an interface, allowing anything that doesn't specifically require one of the actual classes to take a Matrix&.

请在此处查看.

通过这样的设置,通过提供辅助函数,可以轻松动态地创建正确的矩阵.

With a setup like this, it becomes easy to dynamically create the right kind of matrix, by providing a helper function.

// Assume OneDMatrix is expanded for dynamic size specification.
  // It now has constructor OneDMatrix(size_t sz), and owns a dynamic double[sz].
// Assume TwoDMatrix is expanded for dynamic size specification.
  // It now has constructor TwoDMatrix(size_t r, size_t c), and owns a dynamic double[r][c].
Matrix* createMatrix(size_t rows, size_t cols) {
    if (rows == 1)      { return new OneDMatrix(cols);       }
    else if (cols == 1) { return new OneDMatrix(rows);       }
    else                { return new TwoDMatrix(rows, cols); }
}


与往常一样,模板版本更冗长,但更安全(由于不必使用指针),并且效率可能更高(由于不需要进行太多的动态分配和释放);我尚未测试但是).


As usual, the template version is more verbose, but safer (due to not having to play around with pointers) and likely more efficient (due to not needing to do as much dynamic allocation & deallocation; I haven't tested this, however).

这篇关于在运行时更改数组尺寸的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆