假设我有一个基类Base
,两个子类A
和B
,后者本身就是一个子类C
。
我想通过设计强制std::vector
可以保存任何类型的对象(包括C
的子类),但一次只能保存一种类型 - 也就是说,我有一个A
s的向量,或B
s和C
s的向量。
是否有可能在C ++ 11中使用它(当然,我可以在向向量中添加元素时强制执行此约束,但我想知道是否可以通过巧妙的设计来防止这种情况)?如何构造类层次结构来获取它?
提前感谢您的帮助!
R上。
答案 0 :(得分:1)
std::vector<T>
只能拥有一种类型的对象:T
。它永远不能保存派生对象,或者在运行时更改类型。
通过使用间接,您可以单独分配对象,而是在向量中存储指向基类的指针。这允许向量指向派生对象,但它不会阻止同时混合不同类型。
您可以使用标记的联合实现您所描述的内容。 C ++ 11标准库没有标记的联合类型(std::variant
将在C ++中引入17),所以你需要自己实现它(或者你当然可以使用第三方一如既往地实施)。标记的联合允许您列出可以存储的类型,并且一次激活一种类型。因此,您可以使用std::vector<A>
,std::vector<A>
和std::vector<C>
的变体。
那就是说,你没有描述为什么你需要这个,而更好的设计可能是使用一些单独的向量。
答案 1 :(得分:1)
boost::variant< std::vector<A>, std::vector<boost::variant<B,C>> >
描述了您的结构。
在C ++ 17中,将boost::
替换为std::
。两者非常相似,但不完全相同。
如果您无法使用boost
,则必须基本上编写自己的variant
。
使用有点尴尬,但你的类型限制也是如此。使用auto-lambdas在C ++ 14中使用变得更容易(我相信提升变体访问者支持)。
在C ++ 14/17中,您可能会执行类似(伪代码)
的操作template<class F, class T, class=std::enable_if_t<!is_variant<T>{}>>
decltype(auto) visit( F&& f, T&& t ) {
return std::forward<F>(f)(std::forward<T>(t));
}
my_special_vec v;
visit( [&](auto&& v){
for (auto&& e:v) {
Base* b = visit( [&](auto&& e){
e.do_operation();
return std::addressof(e);
}, decltype(e)(e));
b->do_virtual_operation();
}
}, v );
应该在您的向量包含的e.do_operation()
,A
或B
中非虚拟地调用C
,然后在实例上调用do_virtual_operation()
作为Base
。
这是更简单的C ++ 14版本。 C ++ 11版本因功能对象和其他混乱而变得尴尬。