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.