Il punto è che il C++ non consente l'overloading dell'operatore [][] perché [][] non è un operatore, ma la doppia applicazione dell'operatore []. Per poter fare l'"overloading di [][]" bisogna fare l'overloading di [] restituendo un oggetto che a sua volta faccia l'overloading di []. Di solito questo si fa tramite un "proxy object", vedi ad esempio questo mio post:
codice:
template <typename T>
class Container
{
private:
    // ...


public:

    // Proxy object impiegato per fornire il secondo paio di parentesi quadre
    template <typename T>
    class OperatorBracketHelper
    {
        Container<T> & parent;
        size_t firstIndex;
    public:
        OperatorBracketHelper(Container<T> & Parent, size_t FirstIndex) : parent(Parent), firstIndex(FirstIndex) {}

        // Questo è il metodo richiamato per le "seconde parentesi"
        T & operator[](size_t SecondIndex)
        {
            // Chiama il metodo GetElement di parent, che si occuperà di recuperare l'elemento richiesto
            return parent.GetElement(firstIndex, SecondIndex);
        }

    }

    // Questo è il metodo richiamato dalle "prime parentesi"
    OperatorBracketHelper<T> operator[](size_t FirstIndex)
    {
        // Restituisce un proxy object che "sa" a che oggetto deve chiedere l'elemento
        // e qual è il primo indice (specificato in questa chiamata)
        return OperatorBracketHelper<T>(*this, FirstIndex);
    }

    T & GetElement(size_t FirstIndex, size_t SecondIndex)
    {
        // Qui viene effettivamente recuperato l'elemento richiesto
        // ...
    }
};