将vector <derived *>转换为需要向量<base *> </base *> </derived *>的函数

时间:2008-09-22 13:21:03

标签: c++ stl vector covariance

考虑这些课程。

class Base
{
   ...
};

class Derived : public Base
{
   ...
};

这个功能

void BaseFoo( std::vector<Base*>vec )
{
    ...
}

最后我的载体

std::vector<Derived*>derived;

我想将derived传递给函数BaseFoo,但编译器不允许我这样做。如何解决这个问题,而不将整个向量复制到std::vector<Base*>

9 个答案:

答案 0 :(得分:44)

vector<Base*>vector<Derived*>是不相关的类型,因此您无法执行此操作。这在C ++ FAQ here中有解释。

您需要将变量从vector<Derived*>更改为vector<Base*>并将Derived个对象插入其中。

另外,为了避免不必要地复制vector,你应该通过const-reference传递它,而不是通过值:

void BaseFoo( const std::vector<Base*>& vec )
{
    ...
}

最后,为避免内存泄漏,并使代码异常安全,请考虑使用专门用于处理堆分配对象的容器,例如:

#include <boost/ptr_container/ptr_vector.hpp>
boost::ptr_vector<Base> vec;

或者,更改向量以保存智能指针,而不是使用原始指针:

#include <memory>
std::vector< std::shared_ptr<Base*> > vec;

#include <boost/shared_ptr.hpp>
std::vector< boost::shared_ptr<Base*> > vec;

在每种情况下,您都需要相应地修改BaseFoo功能。

答案 1 :(得分:23)

不是传递容器对象(vector<>),而是传递beginend迭代器,就像其他STL算法一样。接收它们的函数将被模板化,如果传入Derived *或Base *则无关紧要。

答案 2 :(得分:14)

在具有可变容器的编程语言中会出现此问题。你不能把一袋可变的苹果作为一袋水果来传递,因为你不能确定别人没有把柠檬放入那袋水果中,之后它就不再是一袋苹果了。如果苹果袋不可变,将它作为一袋水果传递就可以了。搜索协方差/逆变。

答案 3 :(得分:8)

一个选项是使用模板

template<typename T>
void BaseFoo( const std::vector<T*>& vec)
{
 ...
}

缺点是实现必须在标题中,你会得到一些代码膨胀。最后将为每种类型实例化不同的函数,但代码保持不变。根据使用情况,这是一个快速而肮脏的解决方案。

编辑,我应该注意我们在这里需要模板的原因是因为我们正在尝试为其他几个海报所指出的不相关类型编写相同的代码。模板允许您解决这些确切的问题。我还更新了它以使用const引用。当你不需要副本时,你也应该通过const引用传递像矢量这样的“重”对象,这基本上总是如此。

答案 4 :(得分:2)

通常你会从一个基本指针容器开始,而不是从另一个方向开始。

答案 5 :(得分:2)

从上面得到Matt Price's的答案,假设您事先知道要对函数使用哪些类型,可以在头文件中声明函数模板,然后为这些类型添加显式实例化: / p>

// BaseFoo.h
template<typename T>
void BaseFoo( const std::vector<T*>& vec);

// BaseFoo.cpp
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
{
 ...
}

// Explicit instantiation means no need for definition in the header file.
template void BaseFoo<Base> ( const std::vector<Base*>& vec );
template void BaseFoo<Derived> ( const std::vector<Derived*>& vec );

答案 6 :(得分:1)

如果您与第三方图书馆打交道,这是您唯一的希望,那么您可以这样做:

BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));

否则,请使用其他一个建议来修复您的代码。

答案 7 :(得分:1)

如果std::vector支持您所要求的内容,则可以在不使用任何强制转换的情况下击败C ++类型系统(编辑:ChrisN的链接到C ++ FAQ Lite谈论同一问题):< / p>

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

void pushStuff(std::vector<Base*>& vec) {
    vec.push_back(new Derived2);
    vec.push_back(new Base);
}

...
std::vector<Derived1*> vec;
pushStuff(vec); // Not legal
// Now vec contains a Derived2 and a Base!

由于BaseFoo()函数按值获取向量,因此无法修改传入的原始向量,因此我无法写入。但是如果它需要非const引用并且您使用reinterpret_cast<std::vector<Base*>&>()来传递std::vector<Derived*>,则可能无法获得所需的结果,并且您的程序可能会崩溃。

Java数组支持covariant subtyping,这需要Java do a runtime type check every time you store a value in an array。这也是不可取的。

答案 8 :(得分:0)

它们是不相关的类型 - 你不能。