从标准角度看std :: list前哨节点

时间:2019-05-16 06:36:13

标签: c++ stl iterator language-lawyer

代码示例:


    list<int> mylist{10, 20, 30, 40};
    auto p = mylist.end();
    while (true)
    {
        p++;
        if (p == mylist.end()) // skip sentinel
            continue;
        cout << *p << endl;
    }

我想知道,从标准(C ++ 17,n4810)角度来看,这段代码多少合法? 我正在寻找与上面的示例相关的双向迭代器要求,但是没有运气。

我的问题是:

是否可以通过end(),是实现细节还是标准要求?

2 个答案:

答案 0 :(得分:3)

引用在线提供的最新草案。 [iterator.requirements.general]/7

  

就像一个指向数组的常规指针一样,它保证了有一个指针值指向该数组的最后一个元素,因此对于任何迭代器类型,都有一个指向相应序列的最后一个元素的迭代器值。这些值称为过去值。为其定义了表达式i的迭代器*i的值称为可取消引用。 该库从不认为过去的值是可取消引用的。

我认为,这不仅适用于end(),而且适用于此。请注意,该标准并未明确指出end()绝不可取消引用。

Cpp17Iterator requirements table指出,对于表达式*rr应该是可取消引用的:

enter image description here

过去式迭代器被认为是不可递增的迭代器,对其进行递增(如在while循环开始时所做的那样)会导致行为不确定。

使用std::advance时也会发生类似您尝试做的事情。

Nicolai Josuttis的书"The C++ Standard Library: A Tutorial and Reference"引用如下:

  

请注意,advance()不会检查它是否跨过序列的end()(它无法检查,因为迭代器通常不知道它们在其上操作的容器)。 因此,调用此函数可能会导致未定义的行为,因为未定义序列末尾的运算符++

答案 1 :(得分:2)

您的代码是非法的。您首先将p初始化为过去的迭代器

auto p = mylist.end();

现在您p++。根据{{​​3}}, r++的操作语义是:

{ X tmp = r;
++r;
return tmp; }

根据Table 76++r

  

期望: r是可取消引用的。

根据[Table 74]

  

该库从不假定过去的值是   可取消引用的。

换句话说,像过去那样增加一个过去的迭代器是未定义的行为。