我想要 std::vector
的协变包装器.我的想法是做这样的事情:
- 创建抽象基类
BaseVector<B>
谁的begin
和end
方法只是转发给纯虚函数。 - 创建一个具体的派生类
DerivedVector<B, D>
包裹着std::vector<D>
.它的begin
和end
方法将隐藏基类的方法,并且它将实现基类转发到的虚拟方法。
这样,如果你有一个指向 BaseVector
的指针你可以迭代基类实例,如果你有一个 DerivedVector
您可以迭代派生类实例的指针。
(编辑:显然 BaseVector
不一定支持插入,因为它不知道它包含的对象的类型。也许这意味着“vector ”不是最好的名字为此;我愿意接受建议。谢谢@CygnusX1。)
对于 DerivedVector
begin 和 end 方法类可以直接转发到 std::vector
的。
问题:我如何实现 BaseVector
的方法? begin
和 end
转发?我是否必须编写自己的迭代器类来包装 std::vector
迭代器?
或者:是否有更好或更简单的方法来做到这一点?
A DerivedVector<B, D>
实例需要通过 BaseVector<B>
可用知道 B
的代码中的指针但不是 D
,并且它需要具有与仅持有派生类 vector 相同的性能(如果调用代码知道派生类)
示例用例:
一个图书馆提供了一个BaseWidget
类和一个BaseWidgetPool
类,用户从中派生。由于图书馆的性质,在任何给定的程序中都会有一个 DerivedWidget
类,但是对于使用该库的每个程序,该类都会有所不同。
BaseWidgetPool
类包括迭代池中所有小部件并利用其 BaseWidget
的方法功能。但是每个程序的 DerivedWidgetPool
很可能想要使用 DerivedWidget
其内容的功能。
(我知道我也可以通过制作一个通用的 WidgetPool<T>
类来处理这个问题,但我宁愿将 T
封装到实际使用它的代码部分。)
最佳答案
有了范围,这就不是问题了。它具有多态范围所需的所有 API:
#include <range/v3/all.hpp>
namespace rv = ranges::view;
int main() {
std::vector<Derived> derived_container;
// ... fill it
ranges::any_view<Base*> base_view =
rv::all(derived_container) | rv::transform([](auto& e) -> Base* { return &e; });
// do stuff with `base_view`
}
那么只要 derived_container
存在,您就可以使用 base_view
。
我使用了 any_view
这样范围就被类型删除了。您可以在不需要模板化函数的情况下传递它,也可以在虚函数中传递它。
如果您不能使用标准范围或范围 v3,我会创建一个类似的包装器。因此,不是公开类层次结构,而是将其隐藏在实用程序类中。
当然,any_view
只是为了避免在传递范围类型时模板函数。您可以直接将范围具体化为 vector :
auto base_view =
rv::all(derived_container) | rv::transform([](auto& e) -> Base* { return &e; });
std::vector<Base*> base_container = ranges::to<std::vector>(base_view);
这将避免迭代期间的开销,但需要为新 vector 分配内存。
关于c++ - std::vector 的协变包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56342493/