Home > OS >  Can I inherit from std::array and overload operator []?
Can I inherit from std::array and overload operator []?

Time:10-04

The question is rather straightforward. I'm trying to define a custom array class which has all the features of a normal std::array, but I want to add the ability of using operator [] with a custom type defined in my codebase. One option is to wrap my class around the std::array, something like:

using MyType = double; // just an example

template<typename T, unsigned Size>
class MyArray
{
private:
    std::array<T, Size> m_array{ 0 };
public:
    T& operator [](MyType d) { return m_array[abs(round(d))]; }

    void print()
    {
        for (int i : m_array) {
            std::cout << i << std::endl;
        }
    }
};

int main()
{
    MyType var = 0.5649;
    MyArray<int, 5> vec;
    vec[var] = 1;

    vec.print();

    return 0;
}

and the output is

0 1 0 0 0 

as expected. The downside of this is that I can't access all the rest of the typical std::array interface. So I thought why not let my array inherit from std::array:

using MyType = double; // just an example

template<typename T, unsigned Size>
class MyArray : public std::array<T, Size>
{
public:
    // Here I explicitely cast (*this) to std::array, so my operator [] is defined in terms
    // of the std::array operator []
    T& operator [](MyType var) { return std::array<T, Size>(*this)[abs(round(var))]; }
    void print()
    {
        for (int i : (*this)) {
            std::cout << i << std::endl;
        }
    }
};

int main()
{
    MyType var = 2.123;
    MyArray<int, 5> vec{ 0 };
    vec[var] = 1;

    vec.print();

    return 0;
}

but in this case the output is

0 0 0 0 0

no matter the value or var, which means MyArray isn't working as it should. What am I doing wrong? Is there something fundamentally wrong or impossible when inheriting an std::array?

CodePudding user response:

The inheritance itself is legal, as long as you don't try to delete the derived class through a pointer to the base class, which lacks the virtual destructor.

The problem is that return std::array<T, Size>(*this)[abs(round(var))]; creates a temporary std::array and returns a reference to its element, which immediately becomes dangling, since the temporary is destroyed when the function returns.

You want return std::array<T, Size>::operator[](abs(round(var))); to call the operator[] of the base class. Alternatively, you can do static_cast<std::array<T, Size> &>(*this)[abs(round(var))];.


Also note that braced std::array initialization is wonky when you have nested braces. E.g. std::array<std::pair<int, int>, 2> x = {{1,1},{1,1}}; doesn't work, and requires an extra set of braces: std::array<std::pair<int, int>, 2> x = {{{1,1},{1,1}}};.

But if you add inheritance, this too stops working, and you need yet another pair of braces.

  • Related