问题 rend指向哪里?


为了支持STL的半开放范围的概念,我们可以指出数组的一个接一个的结尾。假设我们有一个三元素的向量。如果 std::vector::iterator 实现为指针,就像发布版本中的情况一样 begin 和 end 指向这些位置:

    +---+---+---+....
    |   |   |   |   .
    +---+---+---+....
      ^           ^
    begin        end

点表示一个过去的结束伪元素。因为没有像开始之前那样的东西,究竟会在哪里 rend 指向?让我说明一下:

    +---+---+---+....
    |   |   |   |   .
    +---+---+---+....
  ^           ^
rend       rbegin

显然,插图是错误的,因为 rend 是一个非法的指针。所以我想实施 std::vector::reverse_iterator 永远不会成为指针,即使在发布版本中也是如此。

我对吗?什么是最有效的实施方式 reverse_iterator 然后?


10723
2017-12-05 09:42


起源



答案:


什么是什么之间的区别 reverse_iterator 逻辑上指向它包含的迭代器指向的内容。从逻辑上讲, rbegin 产生一个指向序列的最后一个元素的迭代器 rend 产生一个迭代器,指向开始前的一个元素。但这通常使用一个基本迭代器实现,该迭代器指向反向迭代器指向的元素之后的一个元素。像这样的东西:

template<class Iter>
class reverse_iter
{
    Iter base;
public:
    explicit reverse_iter(Iter it) : base(it) {}

    reference operator*() const {
        Iter tmp = base;
        --tmp;
        return *tmp;
    }

    reverse_iter& operator++() {--base; return *this;}
};

所以,如果你初始化这样的 reverse_iter<> 对象 container.end() 基本迭代器指向一个结束但是反向引用反向迭代器将为您提供最后一个元素。没有伤害。


3
2017-12-05 10:36





因为您不允许取消引用指向容器外部的迭代器,所以实际上并不重要 rend() “指着。它不一定是合法的 指针 value,它可以是对容器/迭代器类型具有特定含义的任何值。


5
2017-12-05 09:44



在C中,如果指针未指向对象或超过对象的最后一个元素的指针,并且该指针被评估(未解除引用),则行为未定义。这个问题是关于C ++的,但我怀疑那里有什么不同。我想你应该澄清你的意思:容器外面的点。 - this
@this:请记住你说的是真的 指针,但不一定是 迭代器。迭代器并不总是指针。 - Greg Hewgill
根据我的理解,这个问题是,如果你没有取消引用它甚至不重要。我们甚至不允许执行指针算术,或者算术计算结果指针'一个在开头之前'。因此,我们不能只创建一个指向数组开头之前的指针,只要我们不取消引用它就可以说它是正常的,因为创建它或使用它来测量大小等的行为是UB。或者我错了? - underscore_d
@underscore_d:你正在考虑实际的规则 指针。另一方面, rend() 返回一个 迭代器 并且标准库以这样的方式实现迭代器,以避免由某些类型的指针操作引起的未定义行为。 - Greg Hewgill
@GregHewgill对,我明白你的意思了。我正在读它“它不一定是一个 法律 指针值“,好像这意味着它可能是一个非法指针值,只要它没有被解除引用...但它听起来像你的意思是它不一定是 任何 指针值,因为它根本不需要是指针。 :-)这是有道理的,并在其他答案中说明。 (另外,正如jcoder所说,编译器和stdlib作者可以勾结各种利用书外实现定义行为的恶作剧,只要用户不必看到任何行为。) - underscore_d


答案:


什么是什么之间的区别 reverse_iterator 逻辑上指向它包含的迭代器指向的内容。从逻辑上讲, rbegin 产生一个指向序列的最后一个元素的迭代器 rend 产生一个迭代器,指向开始前的一个元素。但这通常使用一个基本迭代器实现,该迭代器指向反向迭代器指向的元素之后的一个元素。像这样的东西:

template<class Iter>
class reverse_iter
{
    Iter base;
public:
    explicit reverse_iter(Iter it) : base(it) {}

    reference operator*() const {
        Iter tmp = base;
        --tmp;
        return *tmp;
    }

    reverse_iter& operator++() {--base; return *this;}
};

所以,如果你初始化这样的 reverse_iter<> 对象 container.end() 基本迭代器指向一个结束但是反向引用反向迭代器将为您提供最后一个元素。没有伤害。


3
2017-12-05 10:36





因为您不允许取消引用指向容器外部的迭代器,所以实际上并不重要 rend() “指着。它不一定是合法的 指针 value,它可以是对容器/迭代器类型具有特定含义的任何值。


5
2017-12-05 09:44



在C中,如果指针未指向对象或超过对象的最后一个元素的指针,并且该指针被评估(未解除引用),则行为未定义。这个问题是关于C ++的,但我怀疑那里有什么不同。我想你应该澄清你的意思:容器外面的点。 - this
@this:请记住你说的是真的 指针,但不一定是 迭代器。迭代器并不总是指针。 - Greg Hewgill
根据我的理解,这个问题是,如果你没有取消引用它甚至不重要。我们甚至不允许执行指针算术,或者算术计算结果指针'一个在开头之前'。因此,我们不能只创建一个指向数组开头之前的指针,只要我们不取消引用它就可以说它是正常的,因为创建它或使用它来测量大小等的行为是UB。或者我错了? - underscore_d
@underscore_d:你正在考虑实际的规则 指针。另一方面, rend() 返回一个 迭代器 并且标准库以这样的方式实现迭代器,以避免由某些类型的指针操作引起的未定义行为。 - Greg Hewgill
@GregHewgill对,我明白你的意思了。我正在读它“它不一定是一个 法律 指针值“,好像这意味着它可能是一个非法指针值,只要它没有被解除引用...但它听起来像你的意思是它不一定是 任何 指针值,因为它根本不需要是指针。 :-)这是有道理的,并在其他答案中说明。 (另外,正如jcoder所说,编译器和stdlib作者可以勾结各种利用书外实现定义行为的恶作剧,只要用户不必看到任何行为。) - underscore_d


的结果 rbegin 指向相同 end (一个结束),结果 rend 与...相同 begin (第一项)。取消引用反向迭代器时,它将返回对范围中上一项的引用。


5
2017-12-05 10:13





std::reverse_iterator 界面包括一个 .base 检索与原始迭代器相等的迭代器的成员函数。我怀疑他们通常做的只是缓存原始迭代器,并在operator * overload中偏移1。


2
2017-12-05 10:03



是的, 这就是libstdc ++的作用。 - Lightness Races in Orbit


其他答案很好地回答了这个问题。

但我也想知道它是否合法,因为假设一个实现可以在幕后做任何事情,只要它能够工作并符合标准。如果它选择将迭代器实现为指针并选择取消引用那么编译器有责任知道这将起作用。您无法以这种方式自己实现它,但在我看来,编译器作者有特殊许可来执行此操作,因为他们知道未定义的行为将是什么,并且不会直接将这种行为暴露给您,只是通过迭代器接口。


1
2017-12-05 10:44



这是合法的。允许编译器供应商利用他们自己的UB,并且一直这样做。一个常见的例子是 offset_of 宏,通常是根据空指针的算术实现的。 - MSalters