[英]Arithmetic on end() iterator
让 A 成为std::vector<double>
,
这是明确定义的吗?
if(!A.empty())
std::vector<double>::iterator myBack = A.end() - 1;
end
迭代器仅适用于等式和不等式检查吗? 或者只要我留在容器中,我就可以执行一些指针运算?
在我的平台上,此代码有效。 我想知道这是否是便携式的。
它完全有效,因为vector::iterator
是一个随机访问迭代器。 您可以对其执行算术运算,并且它不依赖于平台。
std::vector<double>::iterator it = A.end();
while (it != A.begin()){
--it; //this will skip A.end() and loop will break after processing A.front()
//do something with 'it'
}
但是A.end()
指的是理论上的过去结束元素,因此它不指向元素,因此不应取消引用。 所以最佳实践是使用反向迭代器而不是递减结束迭代器。
for(std::vector<double>::reverse_iterator it = A.rbegin(); it != A.rend(); ++it) {
//do something with 'it'
}
这两个循环做同样的事情,第二个是可以理解的,更简洁的方法。
如果您注意一些特殊情况,这几乎是安全的:
A.end()
为您提供一个迭代器,表示刚好超出std::vector
末尾的位置。 你不应该试图取消对它的引用。
如果向量具有零个元素,则A.end() - 1
定义不明确。 在所有其他情况下,只要您在容器边界内,您确实可以执行指针运算。 请注意,标准保证std::vector
数据是连续的,并且以与 contains 类型的 C++ 数组完全相同的方式打包。 唯一的例外是std::vector<bool>
由于标准指定的紧包装专业化而表现不同。 (请注意,标准不保证sizeof(bool)
具有特定值)。
如果我是你,我会使用A.rbegin()
访问最右边的元素并在继续之前检查返回值并坚持迭代器公式。 忘记std::vector<bool>
专业化太容易了。
我意识到这个问题有点老了,但我被引导到这里关于end() - 1
,我发现现有的答案和评论信息丰富且合理,但由于缺乏引文而无法令人信服,而且我不确定它们是否特定于vector
。 所以我尽可能多地挖掘具体信息,让自己相信这里的答案是正确的。
这篇文章代表了我为确认答案所做的研究,基本上是我的笔记,但我试图让它尽可能连贯,我认为它可能会有用。 如果有什么这里是关闭的,更正,将不胜感激。
这里的TL;DR是的,这里的答案是正确的,不仅对于vector
,而且在更一般的情况下也是如此:
如果容器的迭代器类型满足BidirectionalIterator (并因此提供递减操作),那么以下对于任何容器类型总是有效的,其中e
被初始化为container.end()
的返回值:
!container.empty()
那么--e
是有效的。!container.empty()
则++(--e) == container.end()
为真。如果迭代器也满足RandomAccessIterator ,那么这些更一般的语句是有效的:
e - n
和e -= n
表示[ 0, container.size() ] 中的任何整数ne + n
和e += n
对于[ - container.size() , 0 ] 中的任何整数n 因此,OP 中的vector
示例不仅很好,正如其他答案所述,而且它定义良好,并且保证适用于任何容器类型。
所以现在这是我觉得缺失的一点。 一、从Container要求:
表达 返回类型 语义 状况 复杂 a.end()
(const_) 迭代器 迭代器到a的最后一个元素之后 持续的
这表示“超过最后一个元素”。 但是,这是否意味着end()
是可递减的? 我们需要确定。 下面的项目在这里很重要,我已经给它们编号以供参考:
end()
返回一个过去的端”上面提到的要求。i - n
,根据-=
定义,没有给出约束。r -= n
,根据+=
定义,没有给出约束。r += n
,根据--r
定义n < 0
,没有给出约束。--a
:-- --a
a == ++b
。--(++a) == a
--a == --b
那么a == b
a
和--a
是同一个迭代器实例。size()
在语义上等同于std::distance(begin(), end())
distance
:返回从first
到last
的增量数。打破这个:
(5) 的先决条件指出,要使--a
起作用, a必须是可递减的,并且继续定义迭代器a是可递减的,如果存在b使得++ b == a
。
(1) 的“one over the end”语言似乎暗示如果b是容器中最后一个元素的迭代器,则++ b == end()
。 不过,更令人信服的是,(7) 表明std::distance(begin(), end())
必须工作,因此 (8) 意味着begin()
返回的迭代器必须能够重复递增,直到它等于end()
,这意味着对于非空容器,在某些时候必须存在b使得++ b == end()
。
将这两者结合起来,表明如果!empty()
则end()
总是可递减的,因为总是有b使得++ b == end()
(否则distance(begin(), end())
— 和因此size()
— 不符合其语义要求),这是可递减性的定义。 还要注意,(6) 明确指出可递减迭代器不需要是可解引用的,并且有关于结束迭代器的递减性的注释。
此外,由于end()
在!empty()
时可递减,那么(其中e
被初始化为container.end()
的返回值):
-- e
是有效的,来自 (5)。e += n
对于n <= 0
是有效的,来自 (4)。e -= n
对于n >= 0
是有效的,来自 (3)。e - n
对于n >= 0
是有效的,来自 (2)。+=
、 -=
和-
(对于上面指出的n 的符号)都是根据重复应用--
语义定义的,这将n限制在容器的大小范围内,因为begin()
不可递减(根据定义递减性),最终迭代器必须点击begin()
。 因此,只要在应用它的迭代器之前至少有 1 个元素,OP 中的- 1
就是有效的(来自(2))。
Decrementability vs. dereferenceability :请注意,这是有区别的。 (6) 指出概念是分开的。 可递减性意味着--i
是有效的,可解引用意味着*i
和i->
是有效的。 在 OP 的vector
示例中,虽然end()
是可递减的,但它不可解引用( vector::end()
明确说明了这一点)。
哦,是的,我还编写了一个测试程序作为完整性检查:
#include <boost/core/demangle.hpp>
#include <version>
#if __has_include(<array>) && (__cplusplus >= 201103L)
# include <array>
# define HAVE_ARRAY 1
#endif
#include <vector>
#include <deque>
#include <list>
#include <set> // + multiset
#include <map> // + multimap
#if __has_include(<span>) && (__cpp_lib_span >= 202002L)
# include <span>
# define HAVE_SPAN 1
#endif
#include <typeinfo>
#include <cassert>
#include <cstdio>
#if (__cpp_constexpr < 200704L)
# define constexpr
#endif
using namespace std;
constexpr const int MAGIC = 42;
int extract (const int &v) {
return v;
}
int extract (const pair<int,int> &v) {
assert(v.first == v.second);
return v.first;
}
template <typename C> struct decrementable_end_tester {
C container;
decrementable_end_tester ();
void test () {
printf("%s...\n", boost::core::demangle(typeid(C).name()).c_str());
assert(!container.empty());
{
typename C::iterator b = container.begin();
typename C::iterator e = container.end();
assert(b == --e);
assert(extract(*e) == MAGIC);
assert(container.end() == ++e);
}
{
typename C::iterator b = container.begin();
typename C::iterator e = container.end();
assert(e == ++b);
assert(container.begin() == --b);
assert(extract(*b) == MAGIC);
}
}
};
// i thought templating that would make initialization simpler but i'm not really
// that great with templates so i dunno if i got the most out of it:
template <typename C> decrementable_end_tester<C>::decrementable_end_tester () {
container.insert(container.end(), MAGIC);
}
#if HAVE_ARRAY
template <> decrementable_end_tester<array<int,1> >::decrementable_end_tester () {
container[0] = MAGIC;
}
#endif
#if HAVE_SPAN
static int span_buffer = ~MAGIC;
template <> decrementable_end_tester<span<int,1> >::decrementable_end_tester ()
: container(&span_buffer, 1)
{
container[0] = MAGIC;
}
#endif
template <> decrementable_end_tester<map<int,int> >::decrementable_end_tester () {
container.insert(make_pair(MAGIC, MAGIC));
}
template <> decrementable_end_tester<multimap<int,int> >::decrementable_end_tester () {
container.insert(make_pair(MAGIC, MAGIC));
}
int main () {
// forward_list, unordered_([multi](set|map)) don't use bidirectional iterators.
#if HAVE_ARRAY
decrementable_end_tester<array<int,1> >().test();
#endif
decrementable_end_tester<vector<int> >().test();
decrementable_end_tester<deque<int> >().test();
decrementable_end_tester<list<int> >().test();
decrementable_end_tester<set<int> >().test();
decrementable_end_tester<multiset<int> >().test();
decrementable_end_tester<map<int,int> >().test();
decrementable_end_tester<multimap<int,int> >().test();
#if HAVE_SPAN
decrementable_end_tester<span<int,1> >().test();
#endif
}
应该在不触发任何断言的情况下运行。
我希望那是有帮助的。 几乎所有这些都是我努力说服自己end() - 1
确实有效
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.