传递派生类型的std :: vector,其中需要基类型的std :: vector

时间:2016-11-26 15:58:42

标签: c++ polymorphism

我有一个基类:

class Member
{
    ....
}

和派生类,如下所示:

class Mine_sweeper : private Member
{
    ....
}

然后我有一个容器:

class Population
{
public:
    std::vector<Member>& m_members;
    Population(std::vector<Member>& members);
}

当我使用Population的构造函数和成员向量时,它编译好了。但是,如果我尝试将人口的m_members与Mine_sweepers的向量链接,则编译器会抛出错误。

std::vector<Member> members;
Population pop(members); // this compiles OK

std::vector<Mine_sweeper> sweepers;
Population pop(sweepers); // but here I get an error
main.cpp(23): error C2664: 'Population::Population(std::vector &)' : cannot convert parameter 1 from 'std::vector' to 'std::vector &'

5 个答案:

答案 0 :(得分:2)

你做不到。不是这样的。这是因为std::vector<B>不是来自std::vector<A>,而是B来自A

但是等等......让我们用一个最小的例子简化你的问题:

struct A {};
struct B : A {};
void f(std::vector<A> const&) {}

int main()
{
    std::vector<A> vecA;
    std::vector<B> vecB;
    f(vecA);
    f(vecB); // error
}

此处与示例代码不同,B公开继承自A。您仍然无法向期望std::vector<B> const& 的函数提供std::vector<A> const&,因为不存在从std::vector<B>std::vector<A> 的隐式转换。

在下面的例子中,可以发送一个B const&,其中A const&是预期的,因为C ++提供了从引用到派生类型到对基类型的引用的隐式转换。

struct A {};
struct B : A {};
void f(A const&) {}

int main()
{
    A alice;
    B bob;
    f(alice);
    f(bob);
}

解决方案是使用从BA的现有隐式转换提供显式转换:

struct A {};
struct B : A {};
class Container
{
    std::vector<A*> _items;
public:
    template<class T>
    Container(std::vector<T> v)
        : _items(v.begin(), v.end())
    {}
    // other stuff, see rule of 5
};


int main()
{
    std::vector<A*> vecA;
    std::vector<B*> vecB;

    Container contA(vecA);
    Container contB(vecB);
}

进行基本的工作转换后,您需要了解如何使用std::unique_ptrstd::shared_ptrstd::weak_ptr来处理内存所有权。

答案 1 :(得分:1)

Population类的构造函数接受一个包含Member类数组的向量的参数。你是对的,你可以将派生类传递给以基类为参数,或者从派生到基础的upcast,但你在这里做的是不同的。你没有将派生类发送给一个带有基类参数的函数,你要发送一个不同的无关类型,你发送一个std :: vector&lt;的typeA&GT;想要std :: vector&lt;的TypeB取代。

当你创建std :: vector&lt; INT&GT;在编译时,它将创建向量类,其中包含特定于其如何迭代整数等的所有特定细节。这是一个数组或“整数”。将它传递给一个带有双精度数组的函数是否有意义?然后该函数正在对双精度进行操作,例如,为了移动到下一个双精度,它可能向前移动8个字节而不是4个,这将是错误的。

您必须手动创建两个版本的Population类,或使用模板执行此操作。编辑:看YSC的答案,他模板构造函数

或者,你可能想要一个矢量/数组,它可以包含两种类型的类,Base和Minesweeper。为此,您无法将对象本身存储在数组中,因为这两种类型的对象可能大小不同并且具有不同的成员。所以你可以有一个指向Base类的指针。然后,当您取消引用其中一个指针时,多态性部分用于识别您是指向Base还是Minesweeper。虚函数用于根据对象调用正确的函数。

答案 2 :(得分:1)

Mine_sweepers的矢量不是会员的“类型”矢量,即使Mine_sweeper是“一种”会员。您无法使用vector<Derived>代替vector<Base>

使用vector<unique_ptr<Member>>代替vector<Member>vector<Mine_sweeper>。这使您可以拥有异构的成员群体(即Mine_sweepers和其他成员子类型,同时)。

或者,将Population类转换为类模板:

class Population<T>
{
public:
    std::vector<T>& m_members;
    Population(std::vector<T>& members);
}

这样,一个Population实例可能只包含一种类型的成员。

答案 3 :(得分:0)

您不能仅将派生类对象的向量传递给需要基类对象向量的函数,因为向量不是从向量派生的。

您可以做一些掩饰,得到矢量的一部分,像这样:

vector<Base> converted(base_vec.begin(), base_vec.end());

但这不是很有效,如果调用方法在您的控件中,则可以使用模板:

template <typename T>
void foo(const vector<T>& v) {
    v[0].bar(); // do something with v
}

如果您不希望其他类型带有bar但不是Base的派生类,则可以使用std :: enable_if:

template<typename T, typename Enable=std::enable_if<std::is_base_of<Base, T>::value>>
void foo(const std::vector<T>& v) {
    v[0].bar(); // do something with v
}

整个程序都可以使用gcc main.cc -std=c++11进行编译:

#include <vector>

class Base{
public:
    void bar() const {}
};

class Derived: public Base{
};

template<typename T, typename Enable=std::enable_if<std::is_base_of<T, Base>::value>>
void foo(const std::vector<T>& v) {
    v[0].bar();
}

int main() {
    std::vector<Derived> a(1);
    foo(a);
}

答案 4 :(得分:0)

您也可以将std::transformstd::back_inserter一起使用,请参见[https://stackoverflow.com/a/33522799/638048]