C++ 在C++;?

C++ 在C++;?,c++,inheritance,vector,virtual,covariant,C++,Inheritance,Vector,Virtual,Covariant,尝试执行以下操作时,会出现编译错误: class A { virtual std::vector<A*> test() { /* do something */ }; } class B: public A { virtual std::vector<B*> test() { /* do something */ }; } A类 { 虚拟std::vector test(){/*做点什么*/}; } B类:公共A { 虚拟std::vector tes

尝试执行以下操作时,会出现编译错误:

class A
{
    virtual std::vector<A*> test() { /* do something */ };
}

class B: public A
{
    virtual std::vector<B*> test() { /* do something */ };
}
A类
{
虚拟std::vector test(){/*做点什么*/};
}
B类:公共A
{
虚拟std::vector test(){/*做点什么*/};
}
我假设A和B是协变类型,因此A*和B*也应该是(正确的?)通过推断,我本以为
std::vector
std::vector
也应该是协变的,但情况似乎并非如此。为什么?

模板不会“继承”协方差,因为不同的模板专门化可能完全不相关:

template<class T> struct MD;

//pets
template<> struct MD<A*> 
{
    std::string pet_name;
    int pet_height;
    int pet_weight;
    std::string pet_owner;
};

//vehicles
template<> struct MD<B*>
{
    virtual ~MD() {}
    virtual void fix_motor();
    virtual void drive();
    virtual bool is_in_the_shop()const;
}

std::vector<MD<A*>> get_pets();
模板结构MD;
//宠物
模板结构MD
{
std::字符串pet_名称;
内部pet_高度;
int pet_重量;
标准::字符串宠物_所有者;
};
//车辆
模板结构MD
{
虚拟~MD(){}
虚拟空位固定马达();
虚拟空驱动器();
虚拟商店是商店中的一个常数;
}
std::vector get_pets();

如果
get_pets
返回一个向量,其中一些实际上是车辆,您会有什么感觉?它似乎违背了类型系统的观点,对吗?

协变返回类型允许派生类中重写的虚拟成员函数返回不同类型的对象,只要它可以与基类的返回类型以相同的方式使用。计算机科学家(自Barbara Liskov以来)对“可以以同样的方式使用”有一个理论定义:可替代性

不,
std::vector
不是
std::vector
的子类型,也不应该是

例如,
std::vector
不支持
push_-back(A*)
操作,因此它是不可替代的

C++根本不尝试推断模板的子类型关系。只有当您专门化一个关系并指定一个基类时,该关系才会存在。其中一个原因,即使在理论上是协变的(基本上是只读)的接口上,C++的版本实际上比Liskov替换更强——在C++中,兼容性必须存在于二进制级别。由于相关对象集合的内存布局可能与子对象位置不匹配,因此无法实现这种二进制兼容性。协变返回类型仅限于指针或引用也是二进制兼容性问题的结果。派生对象可能不适合保留给基础实例的空间。。。但是它的指针会。苹果是水果


一袋苹果不是一袋水果。这是因为你可以把梨放在一袋水果里。

< P> C++直接回答了这个问题:“你不必喜欢它,但你必须接受它。” 所以问题是问同样的问题。答案是,虽然一开始允许泛型类型的协变似乎是安全的,特别是派生类型的容器被视为基类型的容器,但这是相当不安全的

考虑以下代码:

class Vehicle {};
class Car : public Vehicle {};
class Boat : public Vehicle {};

void add_boat(vector<Vehicle*>& vehicles) { vehicles.push_back(new Boat()); }

int main()
{
  vector<Car*> cars;
  add_boat(cars);
  // Uh oh, if that worked we now have a Boat in our Cars vector.
  // Fortunately it is not legal to convert vector<Car*> as a vector<Vehicle*> in C++.
}
class车辆{};
车辆类别:公共车辆{};
船只类别:公共车辆{};
void添加_船(向量和车辆){vehicles.push_back(新船());}
int main()
{
矢量汽车;
增加船只(汽车);
//哦,如果那样行的话,我们现在车里有一艘船。
幸运的是,将向量转换为C++中的向量是不合法的。
}

标准定义了C++ 10.3中的C++的协方差(类.Virtual/P7:

)。 重写函数的返回类型应与 重写函数的返回类型或与 函数的类。如果函数
D::f
重写函数
B::f
,如果函数的返回类型 满足以下标准:

  • 两者都是指向类的指针,都是对类的左值引用,或者都是对类的右值引用113
  • B::f
    的返回类型中的类与
    D::f
    的返回类型中的类相同,或者是明确的 返回中类的可访问直接或间接基类 类型
    D::f
  • 两个指针或引用都具有相同的cv限定,返回类型
    D::f
    中的类类型具有相同的cv限定 与返回类型中的类别类型相同或更少的cv资格
    B::f
113不允许对类的多级指针或对类的多级指针的引用


函数在第一点上失败,即使绕过它,在第二点上也会失败-
std::vector
不是
std::vector

协方差仅在返回指针或类引用时发生,并且类通过继承关联

这显然没有发生,既因为
std::vector
不是指针也不是引用,也因为两个
std::vector
s没有父/子关系

现在,我们可以做到这一点

步骤1,创建一个
array\u视图
类。它有一个
begin
end
指针和方法,还有一个
size
方法和所有您可能期望的方法

步骤2,创建一个
共享\u数组\u视图
,该数组视图还拥有一个带有自定义删除器的
共享\u ptr
:在其他方面是相同的。该类还确保它正在查看的数据持续足够长的时间以供查看

第3步,创建一个
range\u视图
,它是一对迭代器并在其上穿衣。对带有所有权令牌的
共享\u范围\u视图执行相同操作。将
array\u视图
修改为
range\u视图
,并提供一些额外的保证(主要是连续迭代器)

第4步,编写一个转换迭代器。这是一种在
value\u type\u 1
上存储迭代器的类型,迭代器调用函数,或隐式转换为上的常量迭代器
class A {
  owning_array_view<A*> test_() { /* do something */ }
  virtual type_erased_range_view<A*> test() { return test_(); };
};

class B: public A {
  owning_array_view<B*> test_() { /* do something */ };
  virtual type_erased_range_view<A*> test() override {
    return convert_range_to<A*>(test_());
  }
};
template <class ApparentElemType>
struct readonly_vector_view_base
{
    struct iter
    {
        virtual std::unique_ptr<iter> clone() const = 0;

        virtual ApparentElemType operator*() const = 0;
        virtual iter& operator++() = 0;
        virtual iter& operator--() = 0;
        virtual bool operator== (const iter& other) const = 0;
        virtual bool operator!= (const iter& other) const = 0;
        virtual ~iter(){}
    };

    virtual std::unique_ptr<iter> begin() = 0;
    virtual std::unique_ptr<iter> end() = 0;

    virtual ~readonly_vector_view_base() {}
};
template <class ApparentElemType>
class readonly_vector_view
{
  public:
    readonly_vector_view(const readonly_vector_view& other) : pimpl(other.pimpl) {}
    readonly_vector_view(std::shared_ptr<readonly_vector_view_base<ApparentElemType>> pimpl_) : pimpl(pimpl_) {}

    typedef typename readonly_vector_view_base<ApparentElemType>::iter iter_base;
    class iter
    {
      public:
        iter(std::unique_ptr<iter_base> it_) : it(it_->clone()) {}
        iter(const iter& other) : it(other.it->clone()) {}
        iter& operator=(iter& other) { it = other.it->clone(); return *this; }

        ApparentElemType operator*() const { return **it; }

        iter& operator++() { ++*it; return *this; }
        iter& operator--() { --*it; return *this; }
        iter operator++(int) { iter n(*this); ++*it; return n; }
        iter operator--(int) { iter n(*this); --*it; return n; }

        bool operator== (const iter& other) const { return *it == *other.it; }
        bool operator!= (const iter& other) const { return *it != *other.it; }
      private:
        std::unique_ptr<iter_base> it;
    };

    iter begin() { return iter(pimpl->begin()); }
    iter end() { return iter(pimpl->end()); }
  private:
    std::shared_ptr<readonly_vector_view_base<ApparentElemType>> pimpl;
};
template <class ElemType, class ApparentElemType>
struct readonly_vector_view_impl : readonly_vector_view_base<ApparentElemType>
{
    typedef typename readonly_vector_view_base<ApparentElemType>::iter iter_base;

    readonly_vector_view_impl(std::shared_ptr<std::vector<ElemType>> vec_) : vec(vec_) {}

    struct iter : iter_base
    {
        std::unique_ptr<iter_base> clone() const { std::unique_ptr<iter_base> x(new iter(it)); return x; }

        iter(typename std::vector<ElemType>::iterator it_) : it(it_) {}

        ApparentElemType operator*() const { return *it; }

        iter& operator++() { ++it; return *this; }
        iter& operator--() { ++it; return *this; }

        bool operator== (const iter_base& other) const {
            const iter* real_other = dynamic_cast<const iter*>(&other);
            return (real_other && it == real_other->it);
        }
        bool operator!= (const iter_base& other) const { return ! (*this == other); }

        typename std::vector<ElemType>::iterator it;
    };

    std::unique_ptr<iter_base> begin() {
        iter* x (new iter(vec->begin()));
        std::unique_ptr<iter_base> y(x);
        return y;
    }
    std::unique_ptr<iter_base> end() {
        iter* x (new iter(vec->end()));;
        std::unique_ptr<iter_base> y(x);
        return y;
    }

    std::shared_ptr<std::vector<ElemType>> vec;
};
class Base
{
   virtual Base* clone_Base() { ... actual impl ... }
   Base* clone() { return clone_Base(); } // note not virtual 
};

class Derived : public Base
{
   virtual Derived* clone_Derived() { ... actual impl ... }
   virtual Base* clone_Base() { return clone_Derived(); }
   Derived* clone() { return clone_Derived(); } // note not virtual 

};
class Base
{
   virtual shared_ptr<Base> clone_Base() { ... actual impl ... }
   shared_ptr<Base> clone() { return clone_Base(); } 
};

class Derived : public Base
{
   virtual shared_ptr<Derived> clone_Derived() { ... actual impl ... }
   virtual shared_ptr<Base> clone_Base() { return clone_Derived(); }
   shared_ptr<Derived> clone() { return clone_Derived(); } 
};
struct A
{
    readonly_vector_view<A*> test() { return test_A(); }
    virtual readonly_vector_view<A*> test_A() = 0;
};

struct B : A
{
    std::shared_ptr<std::vector<B*>> bvec;

    readonly_vector_view<B*> test() { return test_B(); }

    virtual readonly_vector_view<A*> test_A() {
        return readonly_vector_view<A*>(std::make_shared<readonly_vector_view_impl<B*, A*>>(bvec));
    }
    virtual readonly_vector_view<B*> test_B() {
        return readonly_vector_view<B*>(std::make_shared<readonly_vector_view_impl<B*, B*>>(bvec));
    }
};