0

Is there a way to implement an operator like [][] for a 1D array?

I want to change the implementation of a 2D vector to a 1D vector in my code (cause this increases the execution speed by about %50 in my program). 2D vector supports [y][x]. How can I have such functionality for a 1D vector?

I can do it like this:

const size_t Y_Axis { 20 };
const size_t X_Axis { 30 };

std::vector<char> vec( Y_Axis * X_Axis );

size_t row { 5 };
size_t col { 28 };
vec[ row * X_Axis + col ] = 't'; // assign a value to a specific row-column

However, typing this formula multiple times throughout a source file seems like violating DRY (don't repeat yourself). How can I do this in an efficient and idiomatic way? I want to hide the complexity and make things a bit abstracted just like operator[][] of 2D vector does.

digito_evo
  • 3,216
  • 2
  • 14
  • 42
  • 1
    you have auxiliary objects that take one bracket at a time or use operator()(int i, int j) or use a proper multidimensional container library, there are several, here it is mine https://gitlab.com/correaa/boost-multi – alfC Feb 20 '22 at 07:55
  • 3
    With C++23 you will also be able to define an `operator[]` overload for `vec[row, col]`. According to https://en.cppreference.com/w/cpp/compiler_support there is already support on GCC trunk and Clang trunk. – user17732522 Feb 20 '22 at 07:58

1 Answers1

2

C++ does not allow virtual containers. So the operator [] is expected to return a true object of the expected size, if you want all the goodies like true iterators to work smoothly.

Here is a post of mine about the iterator question for multi-dimensional containers and a more general question on Code Review

If you only want to build an operator[](int) that returns something that can accept a second [], it can easily be done for a 2D vector, by returning a plain pointer inside the internal data array of a vector:

template <typename T>
class vec2d {
    std::vector<T> data;
    size_t _cols;

public:
    vec2d(int rows, int cols, T* src = nullptr)
        : data(rows * cols), _cols(cols) {
        if (src != nullptr) {
            for (T& val : data) {
                val = *src++;
            }
        }
    }

    T* operator [] (size_t row) {
        return data.data() + row * _cols;
    }

    const T* operator [] (size_t row) const {
        return data.data() + row * _cols;
    }

    size_t rows() const {
        return data.size() / _cols;
    }

    size_t cols() const {
        return _cols;
    }
};

And here is an example usage:

int main() {
    vec2d<char> v(3, 4, "ABCDEFGHIJKL");

    for (size_t i = 0; i < v.rows(); i++) {
        for (size_t j = 0; j < v.cols(); j++) {
            std::cout << v[i][j] << ' ';
        }
        std::cout << "\n";
    }
}
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thanks so much. This is really simple. I was looking for something like this. – digito_evo Feb 20 '22 at 11:48
  • this works in term of indexing syntax but it lacks other expected functionality, for example `v[i].size()`. the general trick is to return a proxy or reference-like object. see my comment above. – alfC Feb 20 '22 at 12:21
  • C++ **does not allow** proxy containers - except for the ones from the standard library like `vector` which requires tons of specific handling. For example, a proxy iterator because it cannot even be a forward iterator (so no hope to reverse it) while you can use it for direct access. I once tried to build a generic multi-dimensional container and gave up after understanding that it was just not allowed by the language (see the link to Code Review in my answer...) – Serge Ballesta Feb 20 '22 at 13:23