继承问题中的模板

时间:2018-07-14 04:13:43

标签: c++ templates inheritance

我在CPP和Java中都有一个问题。

#include <vector>

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

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

 a = b;  // compiler error
}

由于某些原因,这两个向量不兼容;


详细信息编辑


感谢所有答案。我需要指出,上面的示例是问题的简化版本,不与细节混淆。我真正的问题是一个Java项目,其中我使用包装器类在两个变量之间保持引用连接。

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 */
 }

 }

4 个答案:

答案 0 :(得分:1)

无论vector<x>vector<y>是什么类型,两种类型xy都不是“兼容的”。容器始终是不同的类型。这就是C ++中的规则。

在您使用class B : A {};的示例中,派生类从基类继承私有。因此,它们之间的联系甚至是一个秘密。

class B : public A {};的情况有所不同,现在每个人都可以使用每个B还包含A部分的事实。

那仍然不允许您互相分配不同的向量类型,但是可以允许分别分配每个元素。 B对象也是A

向量类为此具有成员函数,因此您可以这样做

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

这将复制每个A的{​​{1}}部分,并将该副本存储在B-向量中。

它仍然会导致object slicing,并且不会将a变成vector<a>,但是确实会将每个vector<b>中的值(部分)分配给{ {1}}。这种切片是否可以接受当然取决于应用程序。

答案 1 :(得分:1)

={query(Data1!A4:B,"select * where A is not NULL label A '"&Data1!B1&"'");query(Data2!A4:B,"select * where A is not NULL label A '"&Data2!B1&"'");query(Data3!A4:B,"where A is not NULL label A '"&Data3!B1&"'")} 在其元素类型上是不变,这意味着,即使向量之间的关系相关,不同向量之间也没有关系(从继承意义上来说)。有关类型差异的更多信息,请参见this article

答案 2 :(得分:0)

我在这里可以看到两个问题。

首先,您试图分配(在本例中为副本)向量,即使它们只是已声明但尚未初始化。现在,这并不总是造成问题(例如,向量初始化为空),但是最好不要使用它,因为这样可以得到许多未定义的行为。

第二个是,即使BA的派生类,它们仍然是不同的类。

这样,保存a类对象的向量A无法复制保存b类型的类对象的向量B。它们是完全不同的向量。如果您查看std::vector的副本构造函数的文档,就可以看到以下内容:

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

请注意,如果您创建两个A的子类,它们的名称除了名称之外在其他方面都相同,那么甚至会发生这种情况。

要解决此问题,您可以创建一个在初始化重要数据时为您执行复制的功能,如下所示:

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

不过,要执行此操作,您需要将B设为public A。完成后,您可以创建矢量,如下所示:

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);
}

答案 3 :(得分:0)

好的,再次感谢大家的投入,我想你们实际上帮助了我找回自己的路。 L. Kue提供的代码特别有用,即使它需要更通用。并提醒我我已经使用SFML Vector模板解决了此问题。

这是我针对CPP和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

我必须做一些研究,因为Java泛型与C ++模板非常不同。我从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!
     }