Search code examples
c++vectoroperator-overloading

Overloading the indirection operator to match the subscript operator?


I am attempting to write a class that behaves similarly to std::set - in that the elements are all unique, and adding an element already in the set doesn't do anything - but, where the elements in the set are in a custom order like a std::vector, as opposed to being sorted numerically/alphabetically or being completely unsorted like a std::unordered_set.

The way I've done this is by using a std::vector and a std::map together in my class. I actually already have this working how I want, and now I'm just trying to figure out how to add some overloads to more conveniently access the elements.

Here is a minimal example of what I have so far, stripped down to the part relevant to the question:

template <typename ValueType, typename Comparer = std::less<ValueType>> class MappedVector {
    public:
    using VECTOR = std::vector<ValueType>;

    private:
    VECTOR _vector = {};

    public:
    VECTOR get() const {
        return _vector;
    }

    ValueType operator[](ptrdiff_t index) const {
        return get()[index];
    }
};

The get method allows accessing the underlying std::vector, and the subscript operator allows accessing the elements.

I don't want other code to have direct access to the std::vector, because directly adding elements to it would cause it to become out of sync with the std::map I'm using to index it, so the use of VECTOR instead of VECTOR& and ValueType instead of ValueType& is intentional here: I want to return copies from these and to require going through my push and pop methods or assignment operator to modify the elements. (EDIT: I've since rewritten my class to fix this limitation as per the comments, but the main crux of my question is still relevant.)

Anyway, this all works fine. The issue is with the indirection operator. Typically in C++ (unless the operations are overloaded differently) the following two lines of code are equivalent:

std::string element = vec[1];
element = *(vec + 1);

I've already implemented the former, but am scratching my head what overloads I need to use to get the latter. The following compiles:

ValueType operator*() const {
    return get()[0];
}

But this only allows access to the first element. So I can't add one to the pointer to get another element like in the code sample above. If I replace it with what I believe would be correct:

ValueType operator*() const {
    return *get();
}

Now I get this compile error:

Error C2676 binary '+': 'MAPPED_VECTOR' does not define this operator or a conversion to a type acceptable to the predefined operator

I'm lead to believe that I need to overload operators for ++ and -- for this to work, but how exactly do I forward those so they're operating on the vector, and not the this object itself? I don't want to actually do some version of _vector++ on the vector in my class, because that will affect all future accesses and not just the current one. But the indirection operator, to my understanding, happens afterward, so do I need to stash the result of the addition/subtraction, somehow?


Solution

  • The operation being performed on your class is the addition operator as the error message say. I.e. the expression is primarily vec + 1.

    So you need an operator+ function that takes a size_t argument and returns a pointer to the wanted element:

    ValueType const* operator+(size_t index) const
    {
        return &_vector[index];
    }
    

    To adhere to the commutative property of the addition, you should also create a function which allows 1 + vec:

    template <typename ValueType, typename Comparer = std::less<ValueType>>
    ValueType const* operator+(size_t index, MappedVector<ValueType, Comparer> const& vector)
    {
        // Invoke the operator+(size_t) function
        return vector + index;
    }