代码示例:
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(),是实现细节还是标准要求?
答案 0 :(得分:3)
引用在线提供的最新草案。
[iterator.requirements.general]/7
就像一个指向数组的常规指针一样,它保证了有一个指针值指向该数组的最后一个元素,因此对于任何迭代器类型,都有一个指向相应序列的最后一个元素的迭代器值。这些值称为过去值。为其定义了表达式
i
的迭代器*i
的值称为可取消引用。 该库从不认为过去的值是可取消引用的。
我认为,这不仅适用于end()
,而且适用于此。请注意,该标准并未明确指出end()
绝不可取消引用。
Cpp17Iterator requirements table
指出,对于表达式*r
,r
应该是可取消引用的:
过去式迭代器被认为是不可递增的迭代器,对其进行递增(如在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],
该库从不假定过去的值是 可取消引用的。
换句话说,像过去那样增加一个过去的迭代器是未定义的行为。