4

I am making a Pentago game for someone, and I wanted to write a good code, so I decided to use operator overloading.

We have 2 classes; first one is Block class (which represents every single block of the board) and the second one is Set class (which represents a 3*3 table of blocks). Now I want to use Set as a 2d array so I can use set[foo][foo]. Can you help me to make an operator like this?

2
  • 1
    Perhaps operator overloading is not a good choice for this. Just write a method instead Commented Jun 28, 2015 at 9:05
  • If the memory is contiguous, just return a pointer to the address of the first element of that row from operator [] and it should work Commented Jun 28, 2015 at 9:13

5 Answers 5

3

A very simple solution is

struct MyClass {
    int x[3][3];
    int* operator[](int row) { return &(x[row][0]); }
};

i.e. returning an element* from operator[].

This allows using

myinstance[row][col]
Sign up to request clarification or add additional context in comments.

Comments

3

There are (at least) two ways to go here.


The first is to make something like a set_row class, which is a proxy. So you'd have something like

class set
{
public:
    set_row operator[](size_t row)
    {
        // Return a proxy object that just sees the correct row.
        return set_row(internal_buffer_pointer[row]);
    }

    ...
};

where set_row is something like

class set_row
{
public:
     // Ctor takes a row

     // Take a column, and return a reference to the correct column in the row.
     element &operator[](size_t column);
};

From experience (on, ahem, VisualC++), this was slow, as it will need to construct a proxy object for each access.


The second is to forgo operator[], and use operator():

class set
{
public:
    element &operator()(size_t row, size_t col);


    ...
};

It would be nice using operator[], but, unfortunately, you can't do that with it.

5 Comments

You should not return a reference to a temporary in set_row &set::operator[].
@rodrigo Many thanks - you are absolutely correct. Fixed.
Thank you for your reply. The second solution looks better and more straightforward!
@AmiTavory: are you sure that returning a reference to a row-access object is slow? With what compiler? Compiling with -O3 on both g++ and clang++ the generated code is quite efficient (the temporary row-access object indeed is not created at all).
@6502 I'm ashamed to say that it was with Visual C++. Thankfully, it's been years since I've been doing Windows stuff, but it did slow down production code by much. Thanks for the pertinent comment regarding those compilers!
1

There is no operator[][]. If you want to provide those semantics you need to overload operator[] such that it returns another object that also overloads operator[].

Your case can be solved using a vector of vectors:

#include <vector>
#include <cstdint>
#include <iostream>

struct Block
{
    int value = 0;
};

class Set
{
    std::vector<std::vector<Block> > grid;

public:
    Set(): grid(3, std::vector<Block>(3)) {} // 3 x 3

    std::vector<Block>& operator[](std::size_t x) { return grid[x]; }
};

int main()
{
    using std::size_t;

    Set set;

    set[1][1].value = 1;

    for(size_t x = 0; x < 3; ++x)
    {
        for(size_t y = 0; y < 3; ++y)
        {
            std::cout << set[x][y].value << ' ';
        }
        std::cout << '\n';
    }
}

Output:

0 0 0 
0 1 0 
0 0 0 

This works because Set::operator[] returns reference to a std::vector and the std::vector overloads operator[] to return a reference to a Block.

1 Comment

Using a default class that has the [] operator overloaded sounds good. I will use this. thanks!
1

There is no way to supply an operator[][] for a class.

However, if your Set supplies an operator[](), that operator can return a reference to something else that also has an operator[]().

For example;

 class Row
 {
      public:

           Block &operator[](int block_no) {return data[block_no];};

      private:

           std::vector<Block> data;
 };

 class Set
 {
       public:

          Row &operator[](int row_no) {return row[row_no];};

       private:
           std::vector<Row> row;
 };

 int main()
 {
     Set s;
       // assume s is set up appropriately
     Block b = s[2][3];    //  equivalent to s.operator[](2).operator[](3)
 }

Obviously, it is also necessary to do relevant error checking, set up the contents of the classes correctly, etc.

Comments

0

Assuming the memory is contiguous, you can return a pointer to the first element of the row.

Working example

#include <iostream>

class MyType
{
public:
    static const size_t rows = 3;
    static const size_t columns = 3;
    static const size_t size = rows * columns;

    MyType()
    {
        for(size_t index = 0; index < 9; ++index)
        {
            data[index] = index;
        }
    }

    int* operator[](size_t index)
    {
        return &data[rows * index];
    }

private:
    int data[size];
};

int main()
{
    MyType instance;

    std::cout << instance[2][1] << std::endl;
}

2 Comments

That's true. However, note that you forgo the ability to do stuff like check for illegal indexes in debug mode. Personally, I would not do that.
@AmiTavory thanks for the input, I suppose it could be done with iterators instead. This is just the simplest way I could think of.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.