【问题标题】:Is it possible to write standard conforming random access (or at least forward) Integer Iterator?是否可以编写符合标准的随机访问(或至少向前)整数迭代器?
【发布时间】:2016-12-05 03:39:37
【问题描述】:

假设我们有函数f,它接受整数并返回一个单调递增的值。我们希望找到最小的x,这样f(x) >= C。为简单起见,假设答案在[l;r) 范围内。显然我们可以编写自己的二分搜索实现,但我们想使用现有的 (std::patition_point)。

如此幼稚的实现(可能)会起作用:

// f;
std::vector<int> v(r - l);
std::iota(v.begin(), v.end(), l);
answer = l + partition_point(v.begin(), v.end(), [&](int x) {
    return f(x) <= C;
}) - v.begin();

明显的问题是我们必须存储所有的数字,这会占用大量内存并且需要时间来填充数组

下一个逻辑想法是以这种方式将整数包装到迭代器:

struct IntIterator: std::iterator<std::random_access_iterator_tag, int> {
    int current;
    IntIterator(int i) : current(i) {}
    int operator*() const { return current; }
    IntIterator& operator++() { ++current; return *this; }
    IntIterator& operator+=(size_t n) { current += (int)n; return *this; }
    IntIterator operator+(size_t n) const { return current+(int)n;  }
    size_t operator-(IntIterator that) const { return size_t(current - that.current); }
    // others operators to conform 
};

answer = partition_point(IntIterator{l}, IntIterator{r}, [&](int x) {
    return f(x) <= C;
});

这将适用于我的编译器(标准库),但不符合随机访问迭代器,因为

  • operator* 应该返回 std::iterator_traits&lt;IntIterator&gt;::reference
  • 如果我们将std::iterator_traits&lt;IntIterator&gt;::reference 更改为int(例如通过将模板参数更改为std::iterator)将无法满足前向迭代器的要求:

    如果 X 是一个可变迭代器,reference 是对 T 的引用;如果 X 是 const 迭代器,reference 是对 const T 的引用,

  • 如果我们将operator*的返回类型更改为[const ]T&amp;,它将无法满足前向迭代器的另一个要求

    如果 a 和 b 都是可解引用的,则 a == b 当且仅当 *a*b 绑定到同一个对象。

所以我不明白如何使其符合并提出是否可能的问题。

【问题讨论】:

  • 你说天真的实现可能会起作用是什么意思?你没试过吗?
  • 我可以想办法解决这里的所有问题,除了 operator* 返回的引用的生命周期绑定到所有与取消引用迭代器比较相等的迭代器的生命周期。 IE。如果被取消引用的迭代器是该值的唯一迭代器,那么当迭代器本身超出范围,或者如果它发生变化(递增、递减),则先前返回的引用不再有效。尚未检查这是否会违反随机访问迭代器的要求。
  • @VeniVidiVici 这意味着它会工作,除非我打错字
  • @SamVarshavchik 如果你有时间分享,我想看看你的想法
  • Yakk 偷了我的答案 :-)

标签: c++ iterator language-lawyer


【解决方案1】:

迭代器共享一个指向缓存映射的指针。

缓存映射将索引映射到 (count, value),其中 count 是该索引处的迭代器数。

迭代器记住它是否调用了*

当调用* 时,如果缺少该值,则会填充该值。无论如何,count 会增加,然后返回对 value 的引用。

当迭代器被销毁或迭代器移动(++)时,如果调用了*,则计数递减。

如果计数减为零,则删除该条目。

这会缓存同一索引的所有有效迭代器在其集体重叠生命周期内的值。

【讨论】:

  • 此外,如果迭代器本身发生变化(递增、递减等),引用计数需要递减等。
  • 取一个迭代器,复制它。两者都增加。比较它们:它们应该相等。取消引用之一。这将在缓存中创建条目。保存引用值的地址。摧毁它。这会将计数器递减到 0 并破坏条目。取消引用第二个迭代器。它将在缓存中创建另一个条目,并且其地址不会与存储的条目相比较 → 违反要求。你实际上应该不会遇到任何问题,但是严格来说,ForwardIterator 的概念对于这样的生成器迭代器来说太严格了,我认为不可能完全实现它。
  • @revol 是模棱两可的,因为当您检测到违规时,a 不再存在。您必须“穿越”回到a 存在的时间,或者假设(超出标准要求)引用不会被迭代器破坏/修改破坏/失效。标准中是否有超出迭代器生命周期的参考生命周期的要求?无论如何,即使是这样也可以通过将其设为可选值来解决,无论*如何,都进行计数递增/递减,并在调用*时填充值。
  • 聪明的把戏,谢谢。遗憾的是,在简单循环期间每次迭代都需要分配(而不是简单情况下的增量+返回)
  • @riad 不,您可以在一个数组中有 1-5 个条目,您可以在地图或其他任何地方进行线性搜索。然后前 n 个活着的迭代器立即导致不分配。或者,对具有一些自动存储内存的缓存使用分配器。
【解决方案2】:

如果X 是可变迭代器,reference 是对T 的引用;如果X 是一个常量迭代器,reference 是对const T 的引用

我认为你不应该太认真地对待这个要求。标准库包含一个容器 (std::vector&lt;bool&gt;),其 const_iteratoriterator 未能满足此要求。我知道有a point of view 表示std::vector&lt;bool&gt; 不是容器”,但我更倾向于认为引用的要求过于严格,而不是完全同意这种观点。

【讨论】:

    猜你喜欢
    • 2012-10-27
    • 2017-09-02
    • 1970-01-01
    • 2019-12-25
    • 2015-12-04
    • 2011-07-09
    • 1970-01-01
    • 2020-04-23
    • 2019-12-06
    相关资源
    最近更新 更多