std :: vector

时间:2019-05-28 12:38:32

标签: c++ covariance

我想要std::vector的协变包装。我的想法是做这样的事情:

  • 创建抽象基类BaseVector<B>,其抽象方法beginend仅用于纯虚函数。
  • 创建包装DerivedVector<B, D>的具体派生类std::vector<D>。它的beginend方法将遮盖基类的方法,并将实现基类转发到的虚拟方法。

通过这种方式,如果您有一个指向BaseVector的指针,则可以在基类实例上进行迭代,而如果您有一个DerivedVector指针,则可以在派生的类实例上进行迭代。

EDIT :显然BaseVector不一定支持插入,因为它不知道其包含的对象的类型。也许这意味着“ Vector”不是最好的名字;我愿意接受建议。谢谢@ CygnusX1。)

对于DerivedVector类,begin和end方法可以只转发到std::vector

问题:我如何实现BaseVector beginend的方法?我是否需要编写自己的包装了std::vector迭代器的迭代器类?

或者:是否有更好或更简单的方法来做到这一点?

DerivedVector<B, D>实例需要通过知道BaseVector<B>而不是B的代码中的D指针才能使用,并且其性能必须与保持派生类向量(如果调用代码知道派生类)

示例用例

一个库提供了一个BaseWidget类和一个BaseWidgetPool类,它们是用户派生的。由于该库的性质,在任何给定程序中,都将只有一个DerivedWidget类,但是对于使用该库的每个程序,该类都会有所不同。

BaseWidgetPool类包括迭代池中所有小部件并利用其BaseWidget功能的方法。但是每个程序的DerivedWidgetPool可能都想使用其内容的DerivedWidget功能。

(我知道我也可以通过创建通用的WidgetPool<T>类来处理此问题,但我宁愿将T封装到实际使用它的代码部分。)

1 个答案:

答案 0 :(得分:3)

对于范围,这成为一个非问题。它具有多态范围所需的所有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`
}

然后,只要base_view存在,您就可以使用derived_container

我使用了any_view,因此该区域被类型删除了。您可以传递它而无需对函数进行模板化,也可以在虚拟函数中传递它。

如果您既不能使用标准范围,也不能使用范围v3,那么我将创建一个类似的包装器。因此,您不必将其隐藏在类的类中,而是将其隐藏在实用程序类中。

Live example

当然,any_view只是在传递时避免在范围类型上使用模板函数。您可以直接将范围具体化为向量:

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);

这将避免迭代期间的开销,但需要为新向量分配内存。