[英]Is clamping on iterators valid
我在实际生产代码中发现了以下内容。 我怀疑它实际上具有未定义的行为,但是,我在 cppreference 上找不到相关信息。 您能否确认这是 UB 或有效代码以及为什么这是 UB/有效(最好带有标准的引用)?
#include <vector>
int main(int, char **)
{
auto v = std::vector<int>({1,2,3,4,5});
auto begin = v.begin();
auto outOfRange = begin + 10;
auto end = v.end();
auto clamped = std::min(outOfRange, end);
return (clamped == end) ? 0 : 42;
}
如您所见, begin + 10
将创建一个超出std::vector
范围的迭代器。 但是,没有使用该迭代器,因为它是使用std::min
钳制的。
对于随机访问迭代器, operator+(n)
的操作语义是[random.access.iterators],表 99 *:
difference_type m = n; if (m >= 0) while (m--) ++r; else while (m++) --r; return r;
对于++r
,前提条件是[input.iterators],表 95 *:
前提条件:
r
是可取消引用的。
使用begin() + n
如果n
大于容器的大小,则从m
的某个值开始将不满足此先决条件。 begin + 10;
你已经有UB了,和代码的rest无关。
GCC 标准库清理程序(使用-D_GLIBCXX_DEBUG
编译)会给您以下错误:
/usr/include/c++/10/debug/safe_iterator.h:885:
In function:
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*,
std::__cxx1998::vector<int, std::allocator<int> > >,
std::__debug::vector<int>, std::random_access_iterator_tag>::_Self
__gnu_debug::operator+(const _Self&,
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*,
std::__cxx1998::vector<int, std::allocator<int> > >,
std::__debug::vector<int>,
std::random_access_iterator_tag>::difference_type)
Error: attempt to advance a dereferenceable (start-of-sequence) iterator 10
steps, which falls outside its valid range.
Objects involved in the operation:
iterator @ 0x0x7fffffffb900 {
type = __gnu_cxx::__normal_iterator<int*, std::__cxx1998::vector<int, std::allocator<int> > > (mutable iterator);
state = dereferenceable (start-of-sequence);
references sequence with type 'std::__debug::vector<int, std::allocator<int> >' @ 0x0x7fffffffb8c0
}
好吧,根据标准§5/5.7定义超出范围的迭代器是 UB:
当具有整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。 如果指针操作数指向数组 object 的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果和原始数组元素的下标之差等于整数表达式。 换句话说,如果表达式指向数组 object
i-th
元素,则表达式(P)+N
(等价于N+(P))
和(P)-N
(其中N
具有值 n)指向,分别是数组对象的i+n-th
和第i−n-th
元素,前提是它们存在。 此外,如果表达式P
指向数组 object 的最后一个元素,则表达式(P)+1
指向数组 object 的最后一个元素,如果表达式 Qpointsone 超过数组 ZA8CFDE6331BD49EB2AC96F8911 的最后一个元素(Q)-1
指向数组对象的最后一个元素。 如果指针操作数和结果都指向同一个数组 object 的元素,或数组 object 的最后一个元素,则评估不应产生溢出; 否则,行为未定义
如果您打开 gcc 的迭代器调试,您可以验证这一点
# g++ main.cpp -D_GLIBCXX_DEBUG -o main
# ./main
C:/mingw-w64/i686-8.1.0-win32-dwarf-rt_v6-rev0/mingw32/lib/gcc/i686-w64-mingw32/8.1.0/include/c++/debug/safe_iterator.h:374:
Error: attempt to advance a dereferenceable (start-of-sequence) iterator 10
steps, which falls outside its valid range.
Objects involved in the operation:
iterator @ 0x0061FE3C {
type = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*, std::__cxx1998::vector<int, std::allocator<int> > >, std::__debug::vector<int, std::allocator<int> > > (mutable iterator);
state = dereferenceable (start-of-sequence);
references sequence with type 'std::__debug::vector<int, std::allocator<int> >' @ 0x0061FE50
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.