将vector <Derived *>放入需要vector <Base *>的函数中
考虑这些类。
class Base { ... }; class Derived : public Base { ... };
这个功能
void BaseFoo( std::vector<Base*>vec ) { ... }
最后是我的载体
std::vector<Derived*>derived;
我想将derived
传递给函数BaseFoo
,但是编译器不允许我这样做。我如何解决这个问题,而不将整个向量复制到std :: vector <Base *>
?
解决方案
一种选择是使用模板
template<typename T> void BaseFoo( const std::vector<T*>& vec) { ... }
缺点是实现必须在标头中,我们会有点代码膨胀。我们将为每种类型实例化不同的函数,但是代码保持不变。根据使用情况,这是一种快速而肮脏的解决方案。
编辑,我应该注意这里需要模板的原因是因为我们试图为不相关的类型编写相同的代码,这与其他几个发帖人所指出的一样。模板使我们能够解决这些确切的问题。我还更新了它以使用const引用。当我们不需要复制时,我们也应该通过const引用传递像矢量一样的"重"对象,基本上总是这样。
它们是不相关的类型-我们不能。
通常,我们将从基本指针的容器开始,而不是相反。
" vector <Base *>"和" vector <Derived *>"是不相关的类型,所以我们不能这样做。这在此处的C ++常见问题解答中进行了说明。
我们可以将变量从" vector <Derived *>"更改为" vector <Base *>",然后将" Derived"对象插入其中。
另外,我们应该通过const-reference而不是通过value传递向量:
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
函数。
如果我们正在处理第三方库,而这只是唯一希望,则可以执行以下操作:
BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));
否则,请使用其他建议之一来修正代码。
与传递STL算法的其余部分一样,传递begin
和end
迭代器而不是传递容器对象(vector <>
)。接收它们的函数将被模板化,并且无论我们传入Derived *还是Base *都无关紧要。
如果std :: vector
支持要求,那么就可以在不使用任何强制转换的情况下击败C ++类型系统(编辑:ChrisN到C ++ FAQ Lite的链接也谈到了同一问题):
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()函数按值获取向量,因此它无法修改我们传入的原始向量,因此我写的内容将无法实现。但是,如果使用非常量引用,并且我们使用reinterpret_cast <std :: vector <Base *>&>()
传递std :: vector <Derived *>
,则可能不会得到以下结果:想要,程序可能会崩溃。
Java数组支持协变量子类型,这要求Java每次在数组中存储值时都要进行运行时类型检查。这也是不希望的。
从上面的马特·普赖斯(Matt Price)的答案出发,假设我们已经预先知道要与函数一起使用的类型,则可以在头文件中声明函数模板,然后为这些类型添加显式实例化:
// 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 );
具有可变容器的编程语言中会发生此问题。我们不能将一袋可变的苹果当作一袋水果来回运送,因为我们无法确定别人没有在该袋水果中放入柠檬,之后柠檬就不再符合一袋苹果的资格。如果一袋苹果不易变,将其作为一袋水果随身携带就可以了。搜索协方差/相反。