递减 std::vector::begin 是不是未定义,即使从未使用过?

Posted

技术标签:

【中文标题】递减 std::vector::begin 是不是未定义,即使从未使用过?【英文标题】:Is decrementing std::vector::begin undefined, even if it is never used?递减 std::vector::begin 是否未定义,即使从未使用过? 【发布时间】:2019-11-25 22:10:15 【问题描述】:

请注意,与关于该主题的许多问题相反(可能是为什么我在 google 和 *** 上都找不到这个问题的令人满意的答案),我从不取消引用 *(begin() - 1)


我的要求是:

向后迭代 使用不带反向迭代器的函数,在本例中为vector::erase()

尽量保持代码干净,所以尽量避免以下心理杂耍:

vector.erase(rev_it.base() - 1)

(反向迭代器现在应该是什么来获取迭代中的下一个元素?erase() 返回的迭代器?+ 1,可能?- 1,不太可能?)

我想出的是:

for (auto it = vector.end(); it-- != vector.begin(); ) 
    if (condition(*it)) 
        it = vector.erase(it);
    

这似乎可行,因为it-- 返回迭代器的值,而 then 只会递减它,这意味着迭代器总是在 在检查之后在之前递减进入循环体

特别是:

进入循环时

如果vector.end() == vector.begin()向量为空,我们立即退出循环 如果vector.end() != vector.begin()然后我们进入循环,第一个循环体执行it == vector.end() - 1

擦除元素时

vector.erase(it) 返回向量中的以下元素,因此在每次迭代时递减迭代器让我们只考虑向量中的每个元素。

退出循环时

在最后一次执行循环体时,it == vector.begin(),所以下一次我们尝试循环条件:

条件返回false it 最后一次减一 我们退出循环

也就是说,我的代码确实计算迭代器位置begin() - 1,但从不访问它也不使用它进行比较或类似的东西。

这是未定义的行为吗?

我会冒段错误或其他风险吗?或者只是访问未初始化的数据?什么都没有,因为迭代器在被使用之前就被丢弃了?没有办法知道吗?

【问题讨论】:

因遗漏未定义。无论如何,为什么不使用反向迭代器? “意味着迭代器总是在检查之后但在进入循环体之前递减” - 这根本不是真的。迭代器被复制到一个临时,然后递减,然后临时副本按值返回。这是可以实现迭代器类上的后减运算符的唯一(也是正确的)方式。迭代器在检查前处于 UB 土地;只是不是迭代器开始参与检查;副本是。 @WhozCraig 它只是出现那样,实际情况并非如此,你说的完全正确。让我编辑它。这不会改变我的问题的底线:这个递减的未定义迭代器重要吗? 最终你的问题是,在序列开始时(即 begin())对双向迭代器应用减量操作是否合法。据我所知,唯一支持它的容器迭代器是std::forward_list,它支持before_begin() 迭代器的构造。顺便说一句,你不是第一个问这个问题的人。 See here。建议与此处相同:反向迭代器。 @WhozCraig 实际上我可能已经将球门柱移动了一点,我会恢复它。你的第一条评论是我正在寻找的或真正的。即使我似乎不依赖它当前的行为,也愿意用一两个词来说明为什么 UB 不好? 【参考方案1】:

怎么样

for (auto it = vector.end(); it != vector.begin(); ) 
    --it;
    ... rest of the loop body

【讨论】:

以上是关于递减 std::vector::begin 是不是未定义,即使从未使用过?的主要内容,如果未能解决你的问题,请参考以下文章

将递增/递减运算符放在三元/条件运算符中是不是安全?

Javascript中的递增/递减运算符是不是被认为是错误的形式? [复制]

C ++迭代器取消引用和前缀递增/递减样式? *--Iter ok 风格是不是明智?

从输入类型号递增而不是递减值

shared_ptrs 是不是由于引用计数器原子递增/递减而遇到缓存未命中?

在啥情况下(如果有),C 中前缀和后缀递增/递减运算符之间的性能是不是存在差异? [复制]