0

I have a problem that in both CPP and Java.

#include <vector>

class A {};
class B : A {};

int main()
{
  std::vector<A> a;
 std::vector<B> b;

 a = b;  // compiler error
}

For some reason these two vectors are not compatible;


EDIT FOR DETAILS


Thanks for all the answers. I needed to point out that the example above was a simplified version of problem to not confuse with details. My real problem is a Java project, in which I am using a wrapper class to keep a reference connection between two variables.

Printable.java

package ConsoleGraphics;
public class Printable  { /** code */  }

StringPrintable.java

 package ConsoleGraphics;
 public class StringPrintable { /** code */}

Iterator.java

 package Jav.util;

 public abstract 
 class Iterator<T> implements java.util.Iterator<T>
 {  /** code */  }

Copy_Iter.java

 package Jav.util;

 // An Iterator keeping deep copies of elements
 public class Copy_Iter<T> extends Iterator<T>
 {  /** code, defines abstract methods */ }

Ref_Iter.java

 package Jav.util;

 // Iterator keeping references of the elements.
 public class Ref_Iter<T> extends Iterator<T>
 { /** code, defines abstract methods */ }

Box.java

 package Jav.util;
 public class Box<T> extends Copy_Iter<T>
 { /** code */ }

RefBox.java

 package Jav.util;
 public class RefBox<T> extends Ref_Iter<T>
 {  /** code */ }

Screen.java

 package ConsoleGraphics;

 // This class creates my problem
 public class Screen
 {
   // data members
   private Jav.util.RefBox<Printable> p1;

   private Jav.util.RefBox< 
                               Jav.util.Box <Printable> >p2;

    /** ctors and methods */


    // METHOD OF CONCERN
    // As you can see this function accepts a
    // Box containing Printable. If I try to feed it a 
    // Box containing StringPrintable I fail. But if I
    // create a seperate method for StringPrintable
    // That would mean creating a separate method
    // for every class that inherits from Printable.
    //
    // The aim is for screen class to keep a 
    // reference to the Box object added it. That 
    // when that box increases or decreases,
   // Screen classes Box will do the same.
    // In CPP I wouldn't need the Box wrapper class 
   // and would just use pointers to pointers.

    public void 
    addPrintable(Jav.util.Box<Printable> p)
            {
     // pushBack was declared in Jav.util.Iterator
               p2.pushBack(p);
             }
  }

Main.java

 package main;   // easier to make jar file

 import ConsoleGraphics.*;
 import Jav.util.*;

 public class Main
 {

 public static void main(String[] args)
 {
   Box<StringPrintable> nums = new Box<>();

   Screen sodoku_game = new Screen();

    // error no matching function!
   sudoku_game.addPrintable(nums); 
 }

 // Now imagine if someone inherits 
 class TransformableChars extends Printable
 {
   /** extends code with techniques to make
        Printable Transformable */
 }

 }
  • 2
    The reason is because a `B` may also be an `A`, but vectors of concrete instances of said-same are *not*. They're different, unrelated types. What they *contain* may be related, but what they *are* is *not*. – WhozCraig Jul 14 '18 at 04:19

4 Answers4

1

Two types vector<x> and vector<y> are never "compatible", no matter what types x and y are. The containers are always distinct types. That's the rules in C++.

In your example with class B : A {}; the derived class inherits privately from the base. So it is even a secret that they are related.

This had been different for class B : public A {};, where everyone is now allowed to use the fact that each B also contains an A-part.

That still doesn't allow you to assign the distinct vector types to each other, but it does allow assigning each element individually. A B-object is also an A.

The vector class has a member function for that, so you can do

a.assign(b.begin(), b.end());

This copies the A-part of each B and stores that copy in the a-vector.

It still causes object slicing and doesn't turn a vector<a> into a vector<b>, but it does assign (parts) of the values from each B to an A. If this slicing is acceptable of course depends on the application.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
1

std::vector is invariant on its element type which means that there is no relation (in the inheritance sense) between different vectors even if their elements are related. For more information about type variance please see this article.

r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
0

There are two issues I can see here.

First of all, you're trying to assign (in this case copy) the vectors, even though they have only been declared, but not initialized. Now this doesn't always pose an issue (vectors, for instance, are initialized empty), but it's best to abstain from it, as you can get a lot of undefined behavior this way.

The second is, that even though B is a derived class from A, they are still different classes.

As such, the vector a, which holds class objects of A cannot copy the vector b, which holds class objects of type B. They are entirely different vectors. You can see this if you have a look at the documentation of the std::vector's copy constructor:

@param  __x  A %vector of identical element and allocator types.

Note that this will even happen if you make two subclasses of A which are identical in every way but their name.

To go around this issue, you could create a function that does the copying for you, while initializing the important data, something like this:

void copyAtoB(const A& a, B& b) {
    b.data = a.data;
}

To do this, though, you'd need to make B a public A. Once you've done that you can create the vector like this:

std::vector<A> a = { /* some data */ };
std::vector<B> b;

for (auto& item : a) {
    auto tempB = new B;
    copyAtoB(item, *tempB);
    b.push_back(*tempB);
}
L. Kue
  • 473
  • 5
  • 19
  • 2
    A default vector is empty; no further initialization is necessary. – chris Jul 14 '18 at 04:32
  • @chris I understand that, but wanted to add it anyway, because the asker appears to be relatively new to C++. – L. Kue Jul 14 '18 at 04:34
  • I am wondering why I can't cast `std::vector` to `std::vector` Isn't it logical to do so? – Sudip Bhattarai Jul 14 '18 at 05:06
  • What with the `new B`? Isn't this supposed to be C++ and not Java? – Bo Persson Jul 14 '18 at 10:19
  • No L .Kue I have been practicing C++ unprofessionally for one and half years. The example I gave was a simplified example to clearly tell my problem. My true problem is a project in Java, where I have a "Printable", " StringPrintable" inheriting from Printable and "Box" which is a templated container. Your solution does not solve my problem because it is hardcoded specific. What If somebody else inherits Printable? Also I need to copy the container as a whole. I think there is a way to do these conversions in CPP but again it is hardcoded. –  Jul 14 '18 at 14:19
0

OK thank everyone again for your input, I think you guys actually helped me to find back my way. The code that L. Kue provided was particular helpful even though it needed to be more generic. And reminded me I had already solved this problem with SFML Vector templates.

Here is my solution for both CPP and Java.

C++

 #include <vector>

 struct A {};
 struct B : A {};
 struct C : B {};

 // Modify vector to do the conversions
 template <class T>
 struct Vector : std::vector<T>
 {
  Vector();
  Vector(std::size_t size);
  Vector(const Vector &copy);

  template <class E>
  Vector(const Vector<E> &copy);


 // Everything seem to work fine without
 // assignment operators but I wrote them in case.

  Vector& operator= (const Vector &copy);

  template <class E>
  Vector& operator= (const Vector &copy);
 };

 template <class T>
 Vector<T>::Vector() : std::vector() {};

 template <class T>
 Vector<T>::Vector(std::size_t size)
       : std::vector<T>(size) {};

 template<class T>
 Vector<T>::Vector(const Vector &copy)
      : std::vector<T>(copy) {};

 template <class T>
 template <class E>
 Vector<T>::Vector(const Vector<E> &copy)
     : std::vector<T>( copy.size() )
 {
      static_assert(std::is_base_of<T,E>::value,
                 "Error : Rvalue is not base of Lvalue");

      auto copy_iter = copy.begin();
      for (auto & iter : *this)
      {
        iter = *copy_iter;  ++copy_iter;
      }
 }

 template <class T>
 Vector& Vector<T>operator= (
                                                 const Vector &copy)
 {
  std::vector<T>::operator=(copy);
 }

  template <class T>
  template <class E>
  Vector& Vector<T>::operator= (
                                                   const Vector &copy)
  {
        static_assert(std::is_base_of<T,E>::value,
                 "Error : Rvalue is not base of Lvalue");

        auto copy_iter = copy.begin();
        for (auto & iter : *this)
        {
         iter = *copy_iter;  ++copy_iter;
        }
  }

 int main()
 {
   Vector<A> a(100);
   Vector<B> b(50);
   Vector<C> c(100);

   Vector<A> a1 = b; 
   a = b;
   a = c;
   b = c;

   b=a; // static assertion fails! Great!
  }

Java

I had to do some research as Java generics are very different from C++ templates. I learnt a lot from www.mindview.net .

  class A {};
  class B extends A {};
  class C extends B {};

  class Box<T>
  {
       @SuppressWarnings("unchecked")
       public Box() { obj = (T[]) new Object[0]; }

       @SuppressWarnings("unchecked")
       public Box(int size, T elem)
       {
        obj = (T[]) new Object[size];
        for(int i=0; i<obj.length; ++i) obj[i] = elem;
       }

       // Trying to achieve the same with a template
       // E and template T will not work.
       @SuppressWarnings("unchecked")
       public Box(Box<? extends T> copy)
       {
         obj = (T[]) new Object[copy.length];

         for(int i=0; i<obj.length; ++i) 
         obj[i] = copy.obj[I];
        }

        @SuppressWarnings("unchecked")
        public void pushBack(T elem)
        {
         // Not efficient way of adding elements
          T[] temp = obj;
          obj = (T[]) new Object[obj.length + 1];

         for(int i=0; i<obj.length-1; ++i)
         obj[i] = temp[i];

         final last_index = obj.length-1;
         obj[last_index] = elem;
        }

        public T get(int index) { return obj[index]; }
        public void clear()        { obj = null; }
        public Object array()    { return obj; }

        private T obj[];
 }

 public class Main
 {
     static void foo(Box<A> a)
     {
      System.out.println("mission accomplished!");
     }

     public static void main(String[] args) 
     {
      Box<B> b = new Box<>();
      Box<C> c = new Box<>();
      Box<A> a = new Box<>(b);
      Box<A> a1 = new Box<>(a);
      a = new Box<>(c);
      b = new Box<>(c);

      foo(a);  
      foo(a1);
      foo( new Box<>(b) );
      foo( new Box<>(c) );

      b = new Box<>(a);  // unresolved compiler error
      c = new Box<>(a); // unresolved compiler error
      c = new Box<>(b); // unresolved compiler error

      a = b; // compiler error from day 1
                 // can't change!
     }