3
#include <iostream>
using namespace std;

template<class T>
class Array {
public: // should be private, big ignore that
   int n;
   T *arr;
public:
   Array(int sz, T initValue) {
      n = sz;
      arr = new T[n];
      for (int i=0; i<n; i++) arr[i] = initValue;
   }

   Array& operator = (const Array& b) {
      if (this!=&b) {
         delete[] arr;
         n = b.n;
         arr = new T[n];
         for (int i=0;i<n;i++) arr[i] = b.arr[i];
      }
      return *this;
   }

   Array operator + (const Array& b) {
      Array res(n, 0);
      for (int i=0; i<n;i++) res.arr[i] = arr[i] + b.arr[i];
      return res;
   }
};

int main()
{
   Array<double> a(10, 1); //Array<double> b(10, 2); // this works
   Array<int> b(10, 2);
   a = b; // error
   for (int i=0; i<10; i++) cout << i << " " << a.arr[i] << "\n";

   Array<double> c(10,0);
   c = a + b; // error if b is <int>, runs if b is <double>
   c = a - b;
   c = a * b;
}

So I have a template class that can takes int, float, double, ...

Intuitively, Array<double> a; Array<int> b; a = b; should be possible because element-wise, we can do a[i] = b[i]. However, I have the conversion error because something is missing.

How can I make a = b; possible? Thank you.

Edit: the point is not about making an Array. It can be a Matrix, 3dArray, etc. It's about assignment of a float template and int template. You can also replace int with float, and float with highPrecisionFloat, for example.

Edit 2: I forgot to mention, I not just only need operator =, but operator + (and - * /, etc) as well. If I user @churill answer, I need to do so for each operator. How can I make conversion from Array to Array implicit?

Huy Le
  • 1,439
  • 4
  • 19
  • 2
    Why build `Array` when both `std::array` and `std::vector` already exist? – Jesper Juhl Feb 13 '20 at 15:03
  • @JesperJuhl for example, to make `Array` convertible to `Array` – Yksisarvinen Feb 13 '20 at 15:03
  • @JesperJuhl it can be Matrix, or 3Darray for example. – Huy Le Feb 13 '20 at 15:05
  • 2
    Beware: you are allocating memory... That means that it should be deallocated in a destructor. As the destructor is not trivial, you will need explicit copy/move constructors and assignment operators ([rule of 5](https://stackoverflow.com/q/4782757/3545273)...) – Serge Ballesta Feb 13 '20 at 15:15
  • Wow I knew the rule of 3, didn't know that it has been updated to rule of 5 :\ Thank you. – Huy Le Feb 13 '20 at 15:22
  • 2
    You may want to implement the [copy-and-swap idiom](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) or make use of the [rule of zero](https://en.cppreference.com/w/cpp/language/rule_of_three#Rule_of_zero) (use a `std::vector` as a member). – Bob__ Feb 13 '20 at 15:36
  • @Bob__ I'd go even step further and recommend `std::unique_ptr` as member, so implicit (costly) copy is illegal. – R2RT Feb 13 '20 at 15:40

1 Answers1

1

In the class template

template<class T>
class Array { ... }

The identifier Array refers actually to Array<T>. You will have to make operator== a template and you probably want to add an explicit cast:

template<typename TOther>
Array<T> &operator = (const Array<TOther>& b) {
    if constexpr (std::is_same<T, TOther>::value) {
        // only check for self-assignment T and TOther are the same type
        if (this == &b)
        {
            return *this;
        }
    }

    delete[] arr;
    n = b.n;
    arr = new T[n];
    for (int i=0;i<n;i++) 
    arr[i] = static_cast<T>(b.arr[i]);

    return *this;
}

Note that std::is_same is from the type_traits-header.

Lukas-T
  • 11,133
  • 3
  • 20
  • 30
  • Note that this will produce a distinct-pointer comparison error if `T` is different from `TOther`. – Daniel Langr Feb 13 '20 at 15:05
  • @DanielLangr Good catch, I (hopefully) fixed that. – Lukas-T Feb 13 '20 at 15:09
  • I think if non-template implementation was left as is, compiler would choose the non-template if not converting types. Then check would be performed in non-template operator and in template version you don't need this check at all. But then one would have code duplication... – Yksisarvinen Feb 13 '20 at 15:10
  • expected primary-expression before constexpr. I added ( ) to the if statement, but there's still one syntax error. I've never used constexpr before so how can I fix it ? – Huy Le Feb 13 '20 at 15:15
  • 2
    @HuyĐứcLê: an alternative (but rather C-ish) way would be to convert the pointers to `void *`: the comparison is always valid and can only be true is both objects share same address. It simply writes `if ((void *)this!=(void *) &b) {` – Serge Ballesta Feb 13 '20 at 15:19
  • @HuyĐứcLê Sorry, forget a `typename` in the template-declaration, maybe that caused the error. Anyway `constexpr` is a C++17 feature, so make sure your compiler is using C++17. – Lukas-T Feb 13 '20 at 15:23
  • @churill oh it's the latest feature. I thought I didn't know something obvious :D – Huy Le Feb 13 '20 at 15:27
  • @SergeBallesta wow that's actually a very elegant solution. I will do that. – Huy Le Feb 13 '20 at 15:27
  • @HuyĐứcLê You can simply remove the `constexpr` specifier and just use the `if`. `std::is_same::value` will still be evaluated at compile-time anyway if I'm not mistaken. – Fareanor Feb 13 '20 at 15:32
  • 2
    @HuyĐứcLê: I would not call it elegant, because it just ignores the type information which is amongst the strengths of C ++. In fact it is an old C programmer's hack. I may perfectly be used, because it requires only one single test, but everytime you want to use this kind of hack, you should wonder what is the benefit and what is the cost. Specifically, it could easily lead to ignore the strict aliasing rule and from there invoke UB. Don't worrry it is correct *here*. – Serge Ballesta Feb 13 '20 at 15:34
  • Yes, the `constexpr` is optional, even if the compiler does not evaluate it at compile time that `if` won't cost much, almost nothing in comparison to the rest of the function. – Lukas-T Feb 13 '20 at 15:41
  • I forgot to mention that c = a + b (edited in the post). Should I ask a new StackOverFlow question ? – Huy Le Feb 13 '20 at 15:45
  • @HuyĐứcLê Or you try to do it yourself first, addition for `Array + Array` works the same way as assignment ;) But if you get stuck I think it's better to ask a new question. – Lukas-T Feb 13 '20 at 15:55
  • well yes. But then I have to do the same thing to -, *, /, ... I wish to do something that can make them all work automatically. Like float a + int b – Huy Le Feb 13 '20 at 15:58
  • There is no way to do this, I'm afraid, you could reduce the code a bit with some template magic. I'm not sure how I would do it. – Lukas-T Feb 13 '20 at 16:15