10

I have a 2D array and I want to define a function that returns the value of the index that the user gives me using operator overloading. In other words:

void MyMatrix::ReturnValue()
{
    int row = 0, col = 0;
    cout << "Return Value From the last Matrix" << endl;
    cout << "----------------------------------" << endl;
    cout << "Please Enter the index: [" << row << "][" << col << "] =" << ((*this).matrix)[row][col] << endl;
}

The operation ((*this).matrix)[row][col] should return an int. I have no idea how to build the operator [][].
Alternatively, I could concatenate a couple of calls to the operator [], but I didn't succeed in it, because the first call to that operaror will return int* and the second one will return int, and it compel to build another operator, and I dont want to do that.

The data matrix is defined like

int** matrix; matrix = new int*[row];
if (matrix == NULL)
{
    cout << "Allocation memory - Failed";
}
for (int i = 0; i < row; i++)//Allocation memory
{
    matrix[i] = new int[col];
    if (matrix[i] == NULL)
    {
        cout << "Allocation memory - Failed";
        return;
    }
}

What can I do? Thank you,

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
LIMA
  • 125
  • 1
  • 1
  • 8
  • 2
    Simply, [such an operator does not exist](http://en.cppreference.com/w/cpp/language/operators), so you cannot overload it. – skypjack Dec 25 '15 at 08:55
  • 1
    Ok, It means that I have to build two function for the operator [], because if I do it like: `(((*this).matrix)[row])[col]` so the first operator will return int* and the second int. – LIMA Dec 25 '15 at 08:58
  • @lilach Show how the data member matrix is defined. – Vlad from Moscow Dec 25 '15 at 09:26
  • `int** matrix; matrix = new int*[row]; if (matrix == NULL) { cout << "Allocation memory - Failed"; } for (int i = 0; i < row; i++)//Allocation memory { matrix[i] = new int[col]; if (matrix[i] == NULL) { cout << "Allocation memory - Failed"; return; } }` – LIMA Dec 25 '15 at 09:58

5 Answers5

13

Simply, such an operator does not exist, so you can not overload it.

A possible solution is to define two classes: the Matrix and the Row.
You can define the operator[] of a Matrix so that it returns a Row, then define the same operator for the Row so that it returns an actual value (int or whatever you want, your Matrix could be also a template).
This way, the statement myMatrix[row][col] will be legal and meaningful.

The same can be done in order to assign a new Row to a Matrix or to change a value in a Row.

* EDIT *

As suggested in the comments, also you should take in consideration to use operator() instead of operator[] for such a case.
This way, there wouldn't be anymore the need for a Row class too.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • I would also mention that overloading operator `()` is an easier route that overloading `[]` to return some custom class with its own operator `[]`. See [the C++ FAQs on this respect](https://isocpp.org/wiki/faq/operator-overloading#matrix-subscript-op) – Javier Martín Dec 25 '15 at 09:49
  • Absolutely, it makes sense, but I've understood that the target of the OP is to use a statement like `myMatrix[row][col]` and that's why I went on with a solution that uses `operator[]`. Anyway, good hint, I'm going to modify the response. – skypjack Dec 25 '15 at 09:52
  • How the LINKER distinguish between the function of ROW and Matrix. both get int. they have the same signing. At the first time the operator get the index row and return pointer to that row(int), than it get index col (int) – LIMA Dec 25 '15 at 10:02
  • 1
    @lilach right, but they belong to different classes. :-) – skypjack Dec 25 '15 at 10:03
  • `const int MyMatrix::operator[](const int index)const` `const int* Row::operator[](const int index)const` – LIMA Dec 25 '15 at 10:07
8

You can define your own operator [] for the class. A straightforward approach can look the following way

#include <iostream>
#include <iomanip>

struct A
{
    enum { Rows = 3, Cols = 4 };
    int matrix[Rows][Cols];
    int ( & operator []( size_t i ) )[Cols]
    {
        return matrix[i];
    }
};

int main()
{
    A a;

    for ( size_t i = 0; i < a.Rows; i++ )
    {
        for ( size_t j = 0; j < a.Cols; j++ ) a[i][j] = a.Cols * i + j;
    }


    for ( size_t i = 0; i < a.Rows; i++ )
    {
        for ( size_t j = 0; j < a.Cols; j++ ) std::cout << std::setw( 2 ) << a[i][j] << ' ';
        std::cout << std::endl;
    }
}

The program output is

 0  1  2  3 
 4  5  6  7 
 8  9 10 11 
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Upvoted, even though his colleagues will hurt him for sure once they see this operator. :-) ... I'm still trying to figure out how and why it works!! Thank you, a Christmas gift to my thirst for knowledge. – skypjack Dec 25 '15 at 09:59
  • 2
    It works because it returns a reference to the selected row of the array, which can then be further indexed with `[]` - that type is `int & [Cols]`, which is the true result type of `matrix[i]` before it decays into a pointer. However, the syntax for the function returning such a reference is ugly and confusing, in the same league of member function pointers, so most people do not bother with it and just return a pointer to the first element of the row in question. – Javier Martín Dec 25 '15 at 10:18
  • 2
    Althoug I appreciate a good WAT here and there, might I suggest the use of a trailing return type – MikeMB Dec 25 '15 at 11:07
  • I am not understand, `matrix[i]` not return `int`, it is pointer for row i. – LIMA Dec 25 '15 at 11:45
  • 1
    @lilach matrix[i] returns reference to a one-dimensional array. So You may apply operator [] a second time that to get an integer. That is this record a[i][j] will be valid. – Vlad from Moscow Dec 25 '15 at 11:50
  • may you explain the syntax of: `int ( & operator []( size_t i ) )[Cols] { return matrix[i]; }` I am trying to implement the function (c++), and I am not understand :1. what is the use of `[Cols]? 2. It make use with another function (`operator[]`)? – LIMA Dec 26 '15 at 08:22
  • 2
    @lilach To make the definition simpler you can introduce either a using alias or a type def for the return type. For example struct A { enum { Rows = 3, Cols = 4 }; using TRow = int ( & )[Cols]; int matrix[Rows][Cols]; TRow operator []( size_t i ) { return matrix[i]; } }; – Vlad from Moscow Dec 26 '15 at 09:19
  • 1
    @lilach Or the following way. This is even more clear struct A { enum { Rows = 3, Cols = 4 }; using TRow = int [Cols]; int matrix[Rows][Cols]; TRow & operator []( size_t i ) { return matrix[i]; } }; – Vlad from Moscow Dec 26 '15 at 09:23
3

I have no idea how to build the operator [][].

Sometimes it is fine to use a different operator, namely ():

int& Matrix::operator () (int x, int y)
{
    return matrix[x][y];
}

const int& Matrix::operator () (int x, int y) const
{
    return matrix[x][y];
}

int diagonal (const Matrix& m, int x)
{
    return m (x, x); // Usage.
}

Advantage:

  • No need to use "intermediate" class like Row or Column.

  • Better control than with Row& Matrix operator (int); where someone could use the Row reference to drop in a row of, say, illegal length. If Matrix should represent a rectangular thing (image, matrix in Algebra) that's a potential source of error.

  • Might be less tedious in higher dimensions, because operator[] needs classes for all lower dimensions.

Disadvantage:

  • Uncommon, different syntax.

  • No more easy replacement of complete rows / columns, if that's desired. However, replacing columns is not easy, anyway, provided you used rows to model (and vice versa).

In either case, there are pros and cons if the number of dimensions are not known at runtime.

emacs drives me nuts
  • 2,785
  • 13
  • 23
0

I was looking for self-tested array replacement... Improved version returns reference or NULL reference and checks boundaries inside.

#include <iostream>
#include <iomanip>

template<typename T, int cols>
class Arr1
{
public:
    Arr1(T (&place)[cols]) : me(place) {};
    const size_t &Cols = cols;
    T &operator [](size_t i)
    {
        if (i < cols && this != NULL) return me[i];
        else {
            printf("Out of bounds !\n");
            T *crash = NULL;
            return *crash;
        }
    }
private:
    T (&me)[cols];
};
template<typename T, int rows, int cols>
class Arr2
{
public:
    const size_t &Rows = rows;
    const size_t &Cols = cols;
    Arr2() {
        ret = NULL;
        for (size_t i = 0; i < rows; i++) // demo - fill member array
        {
            for (size_t j = 0; j < cols; j++) matrix[i][j] = cols * i + j;
        }
    }
    ~Arr2() {
        if (ret) delete ret;
    }
    Arr1<T, cols>(&operator [](size_t i))
    {
        if (ret != NULL) delete ret;
        if (i < rows) {
            ret = new Arr1<T, cols>(matrix[i]);
            return *ret;
        }
        else {
            ret = NULL;
            printf("Out of bounds !\n");
            return *ret;
        }
    }
    //T(&MemberCheck)[rows][cols] = matrix;
private:
    T matrix[rows][cols];
    Arr1<T, cols> *ret;
};
template<typename T,int rows, int cols>
class Arr
{
public:
    const size_t &Rows = rows;
    const size_t &Cols = cols;
    T(&operator [](size_t i))[cols]
    {
        if (i < rows) return matrix[i];
        else {
            printf("Out of bounds !\n");
            T(*crash)[cols] = NULL;
            return *crash;
        }
    }
    T (&MemberCheck)[rows][cols] = matrix;
private:
    T matrix[rows][cols];
};

void main2()
{
    std::cout << "Single object version:" << endl;
    Arr<int, 3, 4> a;

    for (size_t i = 0; i <= a.Rows; i++)
    {
        int *x = &a[i][0];
        if (!x) printf("Fill loop - %i out of bounds...\n", i);
        else for (size_t j = 0; j < a.Cols; j++) a[i][j] = a.Cols * i + j;
    }

    for (size_t i = 0; i < a.Rows; i++)
    {
        for (size_t j = 0; j <= a.Cols; j++) {
            std::cout << std::setw(2) << a[i][j] << ' ';
            if (a.MemberCheck[i][j] != a[i][j])
                printf("Internal error !");
        }
        std::cout << std::endl;
    }

    std::cout << endl << "Double object version:" << endl;

    Arr2<int, 3, 4> a2;
    for (size_t i = 0; i < a2.Rows; i++)
    {
        for (size_t j = 0; j <= a2.Cols; j++) {
            int &x = a2[i][j];
            if (&x)
            {
                x++;
                std::cout << std::setw(2) << a2[i][j] << ' ';
                //if (&a2.MemberCheck[i][j] != &a2[i][j])
                //  printf("Internal error !");
            }
        }
    }
}

Output

Single object version:
Out of bounds !
Fill loop - 3 out of bounds...
 0  1  2  3  4
 4  5  6  7  8
 8  9 10 11 -858993460

Double object version:
 1  2  3  4 Out of bounds !
 5  6  7  8 Out of bounds !
 9 10 11 12 Out of bounds !
Jan
  • 2,178
  • 3
  • 14
  • 26
0

it works fine in the program below

#include<iostream>
using namespace std;
class A{
    public:
    int r,c;
    int** val;
    A()
    {
        r=0;c=0;val=NULL;
    }
    A(int row,int col)
    {
        r=row;c=col;
        int count=0;
        val=new int*[row];
        for(int i=0;i<r;i++){
            val[i]=new int[col];
            for(int j=0;j<c;j++){
                count++;
                val[i][j]=count;
            }
        }
    }
    int* &operator[](int index){
        return val[index];
    }
};
int main(void){
    A a(3,3);
    cout<<a[1][2];
    return 0;
}

here, a[1][2] first computes a[1]-->which returns 2nd row as (int*) type then it's read as (int*)[2] which returns 3rd element of that row.In short,

a[1][2]------>(a[1])[2]------>(val[1])[2]------>val[1][2].