66

Can I define in C++ an array operator that takes multiple arguments? I tried it like this:

const T& operator[](const int i, const int j, const int k) const{ 
    return m_cells[k*m_resSqr+j*m_res+i];
}

T& operator[](const int i, const int j, const int k){ 
    return m_cells[k*m_resSqr+j*m_res+i];       
}

But I'm getting this error:

error C2804 binary operator '[' has too many parameters
Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
genesys
  • 3,557
  • 7
  • 29
  • 25

6 Answers6

70

Prior to C++23, you could not overload operator[] to accept multiple arguments. As a workaround, you instead can overload operator(). (See How do I create a subscript operator for a Matrix class? from the C++ FAQ.)


From C++23, as mentioned in a (deleted) answer by cigien, multiple subscript arguments can be passed to operator[] directly. See this demo from the cppreference page.

cigien
  • 57,834
  • 11
  • 73
  • 112
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • 4
    This answer is rather outdated now. From C++20, commas inside [] are deprecated, and from C++23, multiple subscripts are allowed. I've posted an [answer](https://stackoverflow.com/a/71679797) for this. If you want to update your answer to include mine, go ahead and do so, and I'll delete my answer after that. Ping me if you decide to make that edit. – cigien Mar 30 '22 at 15:13
  • 1
    @cigien Thanks! I'm not a fan of copying parts of other people's answers, but in this case I did so because you suggested it and to give the C++23 solution greater visibility. – jamesdlin Mar 30 '22 at 15:35
  • Note that generally, so long as you have original content in your answer, and you attribute the parts that you copy from other answers, it's fine to update your answer to include additional information. Yes, it's not ideal to copy so much from another answer that it entirely invalidates the other answer, so it's nice to at least leave a comment on the other answer indicating that it's no longer needed, or even better, to ask the author of the other answer for permission first. – cigien Mar 30 '22 at 16:24
48

It is not possible to overload the [] operator to accept multiple arguments, but an alternative is to use the proxy pattern.

In two words: a[x][y], the first expression (a[x]) would return a different type, named proxy type, which would have another operator[]. It would call something like _storedReferenceToOriginalObject->At(x,y) of the original class.

You will not be able to do a[x,y], but I guess you wanted to overload the usual C++-style 2D array syntax anyway.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
  • 32
    @Neil Butterworth: I think there's an implicit "you can't do it, but here's an alternative" contained in the answer. – jason Dec 20 '09 at 17:36
  • 2
    I think he wanted the C 2D-array syntax a[x][y] that would pass parameters to operator[](x,y), no? That would actually make sense. – Pavel Radzivilovsky Dec 20 '09 at 17:39
  • The comp.lang.c++ FAQ addresses this too: http://www.parashift.com/c++-faq-lite/operator-overloading.html#faq-13.11 – jamesdlin Dec 20 '09 at 17:40
  • FAQ link by @jamesdlin is now https://isocpp.org/wiki/faq/operator-overloading#matrix-array-of-array "Why shouldn’t my Matrix class’s interface look like an array-of-array?" – TBBle May 08 '17 at 12:20
  • why is it not possible to do `a[x, y]`? The comma operator can be easily overloaded, like how Boost did – phuclv Feb 21 '19 at 14:02
44

There's a nice little trick you can do with the uniform initialization syntax available in C++11. Instead of taking the index directly, you take a POD.

struct indices
{
  std::size_t i, j, k;
};

T& operator[](indices idx)
{
  return m_cells[idx.k * m_resSqr + idx.j * m_res + idx.i];
}

And then use the new syntax:

my_array<int> arr;
// ...
arr[{1, 2, 3}] = 42;
Jiří Pospíšil
  • 14,296
  • 2
  • 41
  • 52
15

For completeness sake: There is a way to actually use the bracket operator with multiple arguments, if they are not basic data types, namely by overloading the comma operator and not the bracket operator, see the following post about comma overloading:

https://stackoverflow.com/a/18136340/5836981

Disclaimer: in my opinion overloading the comma operator is error prone and renders code more obscure, and is worth considering only in more exotic cases. I added this answer because I came over an instance of this in some code and it took me a while to figure out that the key was not the [] operator (which cannot be overloaded with multiple arguments) but the ,operator.

Community
  • 1
  • 1
tePer
  • 301
  • 2
  • 6
  • 4
    wow i did not know you could have multiple arguments in the bracket by overloading the `operator,` that is genius/evil-genius/probably-terrible-idea-most-of-the-time. – Trevor Boyd Smith Aug 02 '18 at 16:47
5

Edit: as pointed in comment, in C++20 operator comma will be deprecated, so as the answer below.

You can't overload operator[], but you can fake it by overloading operator, instead.

Following your code it becomes:

T& operator,(const int i, const int j, const int k){ 
    return m_cells[k*m_resSqr+j*m_res+i];       
}

now you'll be able to call

something[1, 2, 3]

You can extend it using templates, templates with variadic arguments, std::pair or std::tuple depending on your use case and C++ version

Moia
  • 2,216
  • 1
  • 12
  • 34
  • 3
    For info, the comma operator in a subscript operator has been deprecated in C++20: http://eel.is/c++draft/depr.comma.subscript – dgellow Mar 08 '20 at 20:09
  • 1
    @dgellow: That link is no longer valid, i.e. I think it points at C++23 right now. – einpoklum Nov 06 '21 at 22:54
  • That's too bad... Anyway, you can find an archived version here: https://web.archive.org/web/20210427190752/http://eel.is/c++draft/depr.comma.subscript – dgellow Nov 08 '21 at 10:12
  • 1
    So the point of "in C++20 operator comma will be deprecated" is explained at https://en.cppreference.com/w/cpp/language/operators#Array_subscript_operator : "Since C++23, operator[] can take more than one subscripts" - which addresses the question asked here directly. – Mike Kaganski Mar 03 '22 at 07:45
0

N-dimensional arrays of arbitrary type and size in C++:

This answer is inspired by the answer of Pavel Radzivilovsky, thanks for that. I had a bit of a hard time realizing the implementation, as it was my first stab at recursive templates. I'd like to share what I have done such that others can understand more quickly than I did.

I have written a c++ template class to create a n-dimensional array of arbitrary type and size. It needs to be instantiated with the array type and the number of dimensions. The size can be changed dynamically. I've given below a bare (stripped) working version of how to create a multidimensional array of which the elements can be accessed through successive application of the operator[] (e.g. array[x][y][z]). This version can only handle arrays of dimension n>1. The main function shows how to create a 4-dimensional array of integers as an example.

EDIT: keep in mind that the example below is minimal for readability, in that it does not deallocate the array, nor does it do bounds checking on access. Adding this is trivial, and left to the programmer.

#include <stdio.h>
#include <stdlib.h>

template <typename T, int N>
struct array {
    array<T,N>() : data(NULL), offset((int*) malloc(sizeof(int)*N)){}
    array<T,N>(T *data, int *offset) : data(data), offset(offset){}
    array<T,N-1> operator[](int i){return array<T,N-1>(&data[i*offset[N]], offset);}
    bool resize(int *size){
        offset[N-1] = 1;
        int total_size = size[N-1];
        for(int i = N-2; i >= 0; i--){
            total_size *= size[i];
            offset[i] = offset[i+1]*size[i+1];
        }
        return (data = (T*) realloc (data, total_size*sizeof(T)));
    }
    T *data;
    int *offset;
};

template <typename T>
struct array<T,1>{
    array<T,1>(T *data, int *offset) : data(data){}
    T& operator[](int i){return data[i];}
    T *data;
};

int main () {
    array<int, 4> a;

    // create array with dimensions [1][3][3][7]
    int size[4] = { 1, 3, 3, 7 };
    a.resize(size);               

    a[0][1][2][3] = 123;

    return 0;
}

Enjoy.

gospes
  • 3,819
  • 1
  • 28
  • 31
  • 7
    This is pretty good, but I think it's confusing that it's called `array` and looks just like `std::array` at declaration but has template parameters (and semantics) that differ so very wildly (e.g. dynamically sized!). Consider giving it a different name. Also big _ew_ at C headers, `malloc`, `realloc` and `free` ... wait a minute .... you don't have a `free`! You leak _all_ the data. Nice. – Lightness Races in Orbit Nov 22 '14 at 22:05
  • 3
    @LightnessRacesinOrbit: I guess I still owe you a destructor with a free :), but then I would also feel responsible for catching the seqfaults caused by any out-of-bounds access (e.g. access before resize). As for the C headers.. they take their place as the recipients of my continuing infatuation, i.e., I'm staying true to my roots :) – gospes Nov 24 '14 at 12:21
  • 3
    Then -1 for giving awfully buggy code and dangerous advice to a language newcomer. – Lightness Races in Orbit Nov 24 '14 at 12:32
  • Thank you gospes for making this little perl publicly available. +1 for the didactic example and for the clever use of recursive template. –  Aug 22 '18 at 02:05