【问题标题】:Check whether a reverse iterator has crossed forward iterator or not检查反向迭代器是否已经越过正向迭代器
【发布时间】:2018-03-20 18:53:58
【问题描述】:

我有一个vector<int>::iterator 和一个vector<int>::reverse_iterator,如图所示:

vector<int>::iterator start = array.begin();
vector<int>::reverse_iterator end = array.rend();
while (true)
{
    if (*start == *end && start <= end)
    {
        start++;
        end++;
    }
}

在 while 循环中,我必须检查 start 和 end 的值是否相等以及 start 是否没有越过 end。在start &lt;= end 中这样做会给我错误。有人可以指导我通过正确的方法吗?

错误:

开始

【问题讨论】:

  • 错误是什么?
  • 开始 ::iterator 和 reverse_iterator 类型的表达式。 PS。我在 CLion 中编码
  • 您需要先获取反向迭代器的.base(),然后才能比较它们。这可能会有点棘手,具体取决于您想要做什么。那里有一个有时令人惊讶的off-by-1。
  • rend 指向范围的开始,而不是结束。这是反转范围的结束。
  • @LeonardoAlvesMachado - 迭代器必须是相同类型才能具有可比性。

标签: c++


【解决方案1】:

代码:

std::vector<int> test_vector{0, 1, 2, 3};
std::vector<int>::iterator left_it = test_vector.begin();
std::vector<int>::reverse_iterator right_it = test_vector.rbegin();
while (std::distance(left_it, right_it.base() - 1) > 0) {
  ++left_it;
  ++right_it;
}
std::cout << "Iterators crossed" << std::endl;
std::cout << "*left_it = " << *left_it << ", *right_it = " << *right_it << std::endl;

输出:

Iterators crossed
*left_it = 2, *right_it = 1

我们使用std::distance 结合std::reverse_iterator::base() 来确定两个迭代器的相对位置。如果距离为零,则迭代器已经到达相同的元素;如果距离是负数,他们已经越过了。

(注意:否定情况需要C++11或更高版本,调用std::distance第一个参数“after”第二个是C++11之前未定义的行为强>)。


说明:

为了获得比较反向迭代器和正向迭代器的基础,我们需要使用std::reverse_iterator::base() 函数。但是,由于反向迭代器使用了一种实现技巧(原因见下文),这会给您一个结果,它是您可能期望的右侧的一个元素。

为了简单地演示,我们可以遍历一个向量并检查当前地址与向量第一个元素的地址的偏移量。首先,我们有:

std::vector<int> test_vector{0, 1, 2, 3};
for (auto it = test_vector.begin(); it != test_vector.end(); ++it) {
  std::cout << &*it - &test_vector.front() << " ";
}

按预期输出以下内容。

0 1 2 3

如果我们往回走,输出是相反的:

for (auto rit = test_vector.rbegin(); rit != test_vector.rend(); ++rit) {
  std::cout << &*rit - &test_vector.front() << " ";
}

产量

3 2 1 0

但是,如果我们改为检查 std::reverse_iterator::base() 迭代器的地址,我们会看到不同的结果:

for (auto rit = test_vector.rbegin(); rit != test_vector.rend(); ++rit) {
  std::cout << &*rit.base() - &test_vector.front() << " ";
}

产量

4 3 2 1

所以,我们需要将.base()迭代器的地址减去1,得到正确对应的forwards迭代器。下面给出的文档中的一行证实了这一点(但是,我发现他们的解释不清楚且难以理解,这就是我决定进行实际实验的原因):

那是&amp;*(rit.base() - 1) == &amp;*rit

我无法比Mankarse's comment 更好地总结这一点:

考虑迭代器的一种简单方式是它们是光标 元素之间的位置。前向迭代器将产生 取消引用时光标后的元素,反向迭代器将 取消引用时生成光标之前的元素。相等的 正向和反向迭代器是相同的游标 位置。



为什么要减一?

反向迭代器具有这种与生俱来的非 1 差异,您可能会觉得很奇怪。似乎当反向迭代器位于位置i 时,它实际上指向位置i-1 的元素,即元素之前 i

迭代器中有一种天生的不对称性可以解释这一点。考虑前向迭代器:我们可以拥有的“最早”和“最新”迭代器是什么?

最早的迭代器是指向集合的第一个元素的迭代器,而最新的迭代器指向集合的最后一个元素之后

当我们反向迭代时,我们需要反向的相同功能。我们最早的反向迭代器应该直接指向最后一个元素,而我们最新的反向迭代器必须第一个元素之前(或者从相反的角度来看,在第一个元素之后)。这些是允许我们遍历集合并知道我们何时访问过每个元素的基本语义。

我们现在看到正向视图和反向视图之间的自然对应导致了这种一对一的效果。如果我们设想我们的集合从左到右排序,最后一个元素位于倒数第二个位置,按前向顺序访问,但最右端位置以相反的顺序访问。但是,第一个元素正向位于最左位置,倒序位于最左第二个位置。

这是一个具体的例子。当我们的反向迭代器的base 对应于位置0 时,这表明它已经越过了起点(或反向终点)并完成了迭代。所以它指向位置0的元素 前一步,当它的base 位于1 位置时发生。然而,前向迭代器只是在位置0 处引用元素,而它位于位置0

【讨论】:

  • 应该被接受为 imo 的最佳答案
【解决方案2】:

一个连续的迭代器与一个指针包装器相比并不多,找到指向的对象只是获取迭代器指向的对象的地址的问题。所以你可以这样做:

if (*start == *end && &(*start) <= &(*end)) { ...

如果您在像std::arraystd::vector 这样的连续容器上进行迭代,则指针实际上指向相同底层原始数组的元素,因此比较指针是有效的。如果你在一个不连续的容器上进行迭代,那么比较迭代器根本没有意义......

【讨论】:

  • 比较迭代器对于任何随机访问迭代器都非常有意义,而不仅仅是一个连续的迭代器。
  • @DanielH:你确定吗? std::list 不允许快速随机访问其元素,这意味着它们不需要是连续的,因此可以随机分配它们。在这种情况下,比较两个元素的地址将比较两个不相关的对象的地址,这两个对象的行为明确地是未定义的。
  • 你是对的;我想的只是平等比较,而不是小于比较。我编辑了我的评论。非连续容器不支持指针的直接小于比较(例如,它是未定义的行为,而不仅仅是随机的——你不能用&lt;合法地比较指向不同对象的指针,即使你可以用std::less ),但对其中一些迭代器进行比较仍然是有意义的。
【解决方案3】:

Vokay,我已经解决了,我是这样做的:

    vector<int>::iterator start = array.begin();
    vector<int>::reverse_iterator end = array.rbegin();
    while (true)
    {
        if (*start == *end && start <= end.base())
        {
            start++;
            end++;
        }
        else
            break;
    }

我用 rbegin 替换了 rend,因为这就是问题所在

【讨论】:

  • 第一次提到base()的答案怎么了?
  • 他删除了那个答案
  • 这不太正确;一个反向迭代器 points to a different element 比它的 base 非反向迭代器,所以这会引入一个错误。
猜你喜欢
  • 2010-10-27
  • 2011-01-03
  • 1970-01-01
  • 1970-01-01
  • 2012-10-27
  • 2016-03-01
  • 1970-01-01
  • 2020-01-21
相关资源
最近更新 更多