【问题标题】:Use reverse_iterator with type erasure使用带有类型擦除的 reverse_iterator
【发布时间】:2016-05-03 16:53:22
【问题描述】:

我有一个包含和管理一系列对象的类。为了避免在允许迭代它们的同时泄露这些对象的存储方式,我决定使用boost::any_iterator 进行类型擦除。

 using my_erased_type_iterator = boost::range_detail::any_iterator<
    MyClass,
    boost::bidirectional_traversal_tag,
    MyClass&, 
    std::ptrdiff_t>;

我在MyClass 中定义了一个函数Begin()End(),它只是将容器的begin()end() 函数作为my_erased_type_iterator 返回。它完全按照我的意愿工作,MyClass 之外的任何人都不知道我正在使用向量来存储对象,除了我在Myclass 的界面中公开的功能之外,他们也无法访问容器。

现在,出于多种原因,我需要对对象进行反向迭代。我还需要知道反向迭代器之后的下一个元素(类似于在普通迭代器上调用 std::next(),这对于反向迭代器来说已经不是那么简单了),我可能还需要在该反向迭代器上调用像 erase() 这样的函数.

所以对于我的问题:有没有一种优雅的方式来使用类型擦除和反向迭代器(以及正向和反向的 const 版本)?我应该使用前向类型擦除迭代器并向后迭代吗?我突然想到我可能以错误的方式解决这个问题,所以我愿意接受任何建议或在需要时澄清我的问题。

【问题讨论】:

  • 您确实明白您的选择有性能成本,并且迭代器失效规则(每个容器都非常独特)泄漏您正在使用的容器? (当您的迭代器无效时是您界面的一部分)
  • 啊。我现在看到与erase() 相关的请求 - 由于所有“类型擦除”,我错过了它。您不能将erase 与“烘焙”迭代器一起使用,因为没有(unhacky)方法可以返回底层迭代器。 /cc @Yakk
  • @sehe 叹了口气,他们不包括 std::function::target&lt;T&gt;() 的等价物?懒鬼。

标签: c++ boost iterator reverse-iterator boost-type-erasure


【解决方案1】:

请注意,any_iterator 是一个实现细节。

我将首先回答您的直接问题,然后按照 Boost Range 的公共 API 的意图使用 any_range&lt;&gt; 展示该方法。

1。 make_reverse_iterator

您可以简单地使用来自

make_reverse_iterator设施

Live On Coliru

#include <boost/range.hpp>
#include <boost/range/any_range.hpp>

struct MyClass {
    int i;
};

using my_erased_type_iterator = boost::range_detail::any_iterator<
    MyClass,
    boost::bidirectional_traversal_tag,
    MyClass&, 
    std::ptrdiff_t>;

#include <iostream>
#include <vector>

int main() {
    using namespace boost;
    std::vector<MyClass> const v { {1}, {2}, {3}, {4} };

    for (auto& mc : make_iterator_range(
                make_reverse_iterator(v.end()),
                make_reverse_iterator(v.begin())))
    {
        std::cout << mc.i << " ";
    }
}

打印

4 3 2 1 

2。 reversed 范围适配器:

或者,您可以采用全系列风格并使用any_range&lt;&gt;

Live On Coliru

int main() {
    std::vector<MyClass> const v { {1}, {2}, {3}, {4} };

    boost::any_range_type_generator<decltype(v)>::type x = reverse(v);

    for (my_erased_type_const_iterator f = boost::begin(x), l = boost::end(x); f!=l; ++f) {
        std::cout << f->i << " ";
    }

}

【讨论】:

  • 添加了关于库实现细节的警告,以及更简洁的any_range&lt;&gt; 版本。
  • 不支持擦除?
  • @Yakk 你什么意思?在我的示例中,它们是常量迭代器,并且是双向/随机的,具体取决于用途。想澄清一下?
  • OP 希望使用反向迭代器擦除元素作为擦除应该去的地方(确切的细节是模糊的)。
  • "as where the erase should go" - 我不知道你的句子是什么意思,对不起
【解决方案2】:

只需反转类型擦除的迭代器。

这会暴露.base(),这意味着擦除几乎与擦除类型擦除前的类型一样容易。

顺便说一句,您的设计有性能成本(根据我的经验)边际收益。底层容器的 iterafor invalidadion 规则仍然适用,所以你的类的用户必须知道底层容器是什么! (或者,他们也可能知道这么多)。换出容器不会提供足够相似的行为,因此尽管您尝试隐藏它的成本相当高,但您的容器仍被锁定。

【讨论】:

  • 由于我仍处于开发程序的早期阶段,我想避免将类“绑定”在一起。类型擦除似乎是这样做的好主意。稍后当我有指标和分析器显示性能成本对于设计收益而言太高时,它可能会消失,但现在,我想优先考虑良好的设计而不是性能。
  • @user1784377 当然可以,除非很少有理由使用除 vector 之外的任何东西作为非关联容器。这种擦除可能让您在稍后在同一向量中公开的内容旁边存储其他内容。但是您应该记住的一条规则是“您可能不需要它”:尽早编写一堆(可能成本高昂的)抽象可能会在一年内解决一些问题,但您更有可能不需要那个抽象。项目会死掉,可能是因为过度抽象而死,或者抽象是不需要的。
  • 我确实知道“你可能不需要它”,并且在我真正需要它之前我倾向于避免编码东西,但我想它也适用于设计和抽象。尽管我确实希望该项目在一年内仍然存在,并且我不期待以后添加该抽象,但我认为您可能是对的,并且现在摆脱该额外抽象可能会简化并加速开发作为整个。
猜你喜欢
  • 2012-01-27
  • 1970-01-01
  • 2021-12-28
  • 2016-02-14
  • 1970-01-01
  • 2017-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多