微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

通用结束迭代器与容器 `end()`

如何解决通用结束迭代器与容器 `end()`

我正在研究 ReversibleContainer 及其关联的 LegacyRandomAccessIterators。它们包装了一个预先存在的数据结构,该结构表示一个可直接索引的对象集合。我决定允许迭代器独立存在,并使认构造的迭代器代表“结束”:

// constructs an iterator that provides a view of 'data'
the_iterator (thedata *data,difference_type index = 0);

// constructs an iterator representing the end
the_iterator ();

所以我可以做例如:

std::for_each(the_iterator(data),the_iterator(),...);

迭代器完成所有工作。容器非常轻巧。我像这样实现了容器的 begin()end()

struct the_container {
    the_data *data; // <- object wrapped by this container
    the_iterator begin () { return the_iterator(data); }
    the_iterator end () { return the_iterator(); }
};

我让它工作得很好,但在测试中我意识到我搞砸了,它不符合基本的 Container 要求,因为:

  • 事实证明,对于具有双向迭代器的容器,end() is required to return a decrementable iterator 当容器非空时,但是
  • 认构造的结束迭代器不存储关于任何 thedata 的任何信息,因此它不能递减,因为它不知道任何特定集合的“最后一个元素”是什么。

所以我现在必须修理容器。我正在考虑的解决方案是(让 data->number_of_items 包含项目计数):

  • 继续允许认构造的迭代器表示“结束”。
  • 也让the_iterator(data,data->number_of_items)代表“结束”,符合容器要求。此迭代器将是可递减的。

然后容器会做:

struct the_container {
    the_data *data; // <- object wrapped by this container
    the_iterator begin () { return the_iterator(data,0); }
    the_iterator end () { return the_iterator(data,data->number_of_items); }
};

现在,很好,它满足所有Container要求。但是,我现在想知道是否允许我的认构造迭代器存在。

那么,我的问题是:虽然 Container 对其从 end() 返回的迭代器提出了可递减性要求,但对于仅表示某些数据的“结束”但不涉及容器的 end()

更正式地说,如果:

  • j一个双向迭代器
  • container.empty() == false
  • ( j == container.end() ) == true

那么 --j 是否需要有效并且需要指向容器的最后一个元素?就我而言,这种情况的一个例子是:

the_container container(data); // <- assume data->number_of_items > 0
the_iterator b = container.begin();
the_iterator e = container.end();
the_iterator j;

assert(container.empty() == false);
assert(e == j);
assert(distance(b,e) == distance(b,j));

-- e;  // <- this is required to be well-defined
-- j;  // <- but is this??

所以,是的,这就是我的问题。我担心可能会在例如<algorithm> 可能会假设我的“结束”迭代器之一是可递减的,或者我正在破坏一些我不理解的微妙内容

解决方法

作为框架挑战,您在这里拥有的是一个哨兵。哨兵是你可以比较迭代器的东西,但它不是迭代器。

激励示例是一种类型,当与 char 指针进行比较时,它只检查 char 指针的取消引用是否为空。您可以使用它来编写 C 级“读取字符串直到为空”,同时仍然使用 for(:) 循环等。

基于范围的 for(:) 循环得到增强,允许开始和结束具有不同的类型,以允许哨兵存在并最大限度地提高效率。

考虑用结束标记替换默认构造的迭代器技巧。这不适用于许多 pre-ranges-v2 算法,但至少您正在使用经过验证的真实模式。

不,我不认为您的最终迭代器违反了规则。例如,在标准下,来自两个容器的迭代器可能会产生无意义的 == 结果。您的最终迭代器可以被视为来自具有可预测 == 行为的另一个容器的迭代器。

现在,当你将它传递给算法时会发生什么?这可能会使您的程序格式错误;该算法可以检测您提供的特征保证,并且您的假结束迭代器不是与开始迭代器位于同一容器中的有效双向迭代器。

,

那么 --j 是否需要有效并且需要最终指向容器的最后一个元素?

对于任何双向迭代器,只要满足其先决条件,就必须明确定义 --j。前提条件是存在另一个迭代器s,其j == ++s。对于您的容器,最后一个有效元素的迭代器就是这样的迭代器

因此,我不相信您的迭代器会满足双向迭代器的要求,因此容器是不可逆的。

如果你想为双向迭代器定义一个哨兵,可以通过使用一个单独的类型来完成,这样迭代器的要求就不会适用于它。但是有一个警告,大多数标准算法不支持单独的迭代器和哨兵类型。 std::ranges 算法可以。

或者,您可以指定默认初始化迭代器是 singular,即它不与任何容器关联。在这种情况下,-- j 不会有任何要求(j == e 也不会)。但是,b,j 将不是有效范围,并且您仍然不能将此类对传递给标准算法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。