0

I'm building a multi-dimensional array using templates but I'm having problems typecasting to a base class. Here is my code so far.

#include <memory>

struct A {
  int a;
};

struct B : A {
  int b;
};

template<typename T>
struct Array {
  std::shared_ptr<T> t;
  int length;
  T operator[](int x) {
    return t[x];
  }

  Array() {}

  template<typename T2>
  Array(Array<T2> o) {
    t = o.t;
    length = o.length;
  }
};


int main() {
  Array<B> b;

  Array<A> a = b;  //OK

  Array<Array<B>> bb;

  Array<Array<A>> aa = bb;  //Error

  return 0;
}

One-dimension type-cast works, but multi-dimensions fail.

The error I get is

cannot convert 'Array<B>* const' to 'Array<A>*' in assignment

Am I missing an operator or something?
I need something that will work regardless of how many dimensions are created.

The Array class has a lot of functions removed for clarity, I just need the type-cast to work.

Note : the std::shared_ptr::operator[] will require C++17.

Thanks.

Peter Quiring
  • 1,648
  • 1
  • 16
  • 21
  • 1
    never cast to a base class – Cheers and hth. - Alf Mar 20 '18 at 13:35
  • This looks broken. First of all, the shared pointer should be `std::shared_ptr` to hold an array. But then, trying to index an array of derived objects with a pointer to base object cannot work. – Quentin Mar 20 '18 at 13:35
  • 1
    Code review: `t = o.t` doesn't copy the value, it just make another reference to the same value. (as `t`'s type is `std::shared_ptr`) – user202729 Mar 20 '18 at 13:36
  • @Quentin - C++17 does not require that. – Peter Quiring Mar 20 '18 at 13:36
  • Your code looks like that it will segfault, as you never initialized `t`... (unless you intentionally removed unrelated parts to be a MCVE) – user202729 Mar 20 '18 at 13:38
  • @PeterQuiring: Please don't post disinformation, a.k.a. trolling. – Cheers and hth. - Alf Mar 20 '18 at 13:38
  • @user202729 copying shared_ptr is desired – Peter Quiring Mar 20 '18 at 13:38
  • @user202729 I said I left code out for clarity - just focus on the type cast – Peter Quiring Mar 20 '18 at 13:39
  • @Cheersandhth.-Alf ---[`operator[]` is available in C++17](http://en.cppreference.com/w/cpp/memory/shared_ptr/operator_at).--- Nope, need to be type `T[]`. [Try it online!](https://tio.run/##Sy4o0E1PTv7/XzkzLzmnNCXVJjc1N7@o0o4rM69EITcxM09Ds7q4JMXKqjgjsSg1Jb6gpMgGKGVXYg1SkGlbEm0ca137//@/5LScxPTi/7pAxbbJ2tqG5gA "C++ (gcc) – Try It Online") – user202729 Mar 20 '18 at 13:41
  • That about C++17 allowing indexing array of Derived via a pointer to Base. That's just Undefined Behavior. – Cheers and hth. - Alf Mar 20 '18 at 13:41
  • @user202729: `shared_ptr::operator[]` is defined as `Returns: get()[i]`. That's UB when `get()` return a `Base*` and `i` > 0. – Cheers and hth. - Alf Mar 20 '18 at 13:45
  • @Cheersandhth.-Alf [Looks like you're correct](https://stackoverflow.com/questions/13718527/how-to-make-an-array-with-polymorphism-in-c). – user202729 Mar 20 '18 at 13:46
  • @user202729 - I think your right - but then even the first type cast doesn't work – Peter Quiring Mar 20 '18 at 13:46
  • `std::vector` doesn't allow such assignment. That's ok if you will also not support it for your class. – Yola Mar 20 '18 at 13:47
  • @Yola The OP is asking "how to implement it", not "is there any alternative". – user202729 Mar 20 '18 at 13:47
  • 2
    @PeterQuiring yes, that's what I meant. Surely you can overload enough operators so it compiles and looks like it makes sense, but your central feature cannot work because you end up trying to index an array with the wrong type. – Quentin Mar 20 '18 at 13:48
  • @user202729 what i said means that there is no way to implement it, at least without tantamount effort, which will most likely fail in practice. – Yola Mar 20 '18 at 13:50
  • @PeterQuiring So... first try to implement 1D typecast and test it. Your current code has a lot of issues. As Cheersandhth.-Alf said, you can't copy the `shared_ptr`. Then if there are still any problems, ask here. – user202729 Mar 20 '18 at 13:50
  • @user202729 - why can't you copy the shared_ptr? it has the proper operators defined to typecast properly - I've looked as it's source code. That works and is not the issue. – Peter Quiring Mar 20 '18 at 13:54
  • But I agree it should be std::shared_ptr and then nothing works. I'll work on a revision. – Peter Quiring Mar 20 '18 at 13:56
  • As @Cheersandhth.-Alf said, using a pointer of a base class to index into an array of a derived class is undefined behavior. – user202729 Mar 20 '18 at 13:56
  • What is the array was an array of pointers? Then it would work. – Peter Quiring Mar 20 '18 at 14:00
  • Yes, then it would work. But that's not your question. – user202729 Mar 20 '18 at 14:05

1 Answers1

0

Well, I found one possible soln:

#include <memory>

struct A {
  int a;
};

struct B : public A {
  int b;
};

template<typename T>
struct A1 {
  T **t;
  A1() {}
  T operator[](int idx) {
    return *t[idx];
  }
  template<typename T2>
  A1(A1<T2>* o) {
    t = (T*)o->t;
  }
  template<typename T2>
  A1(A1<T2> o) {
    t = (T*)o.t;
  }
};

template<typename T>
struct A2 {
  A1<T> *t;
  A2() {}
  A1<T> operator[](int idx) {
    return t[idx];
  }
  template<typename T2>
  A2(A2<T2>* o) {
    t = (A1<T>*)o->t;
  }
  template<typename T2>
  A2(A2<T2> o) {
    t = (A1<T>*)o.t;
  }
};

template<typename T>
struct A3 {
  A2<T> *t;
  A3() {}
  A2<T> operator[](int idx) {
    return t[idx];
  }
  template<typename T2>
  A3(A3<T2>* o) {
    t = (A2<T>*)o->t;
  }
  template<typename T2>
  A3(A3<T2> o) {
    t = (A2<T>*)o.t;
  }
};

int main() {
  A3<B> b3;
  A3<A> a3 = b3;
  A2<A> a2_ = b3[5];

  A2<A> a2 = a3[1];

  A a = a3[5][2][1];

  A3<int> i1;

  return 0;
}

It requires a class per dimension but I never need more than 3 levels. This allows the type-casting required, uses arrays of pointers so using a base class should work.

Peter Quiring
  • 1,648
  • 1
  • 16
  • 21