钳位迭代器是否有效
Is clamping on iterators valid
我在实际生产代码中发现了以下内容。
我怀疑它实际上有未定义的行为,但是,我在 cppreference 上找不到相关信息。您能否确认这是 UB 或有效代码以及为什么这是 UB/valid(最好引用标准)?
#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], Table 99 *:
difference_type m = n;
if (m >= 0)
while (m--)
++r;
else
while (m++)
--r;
return r;
而对于 ++r
,前提条件是 [input.iterators], Table 95 *:
Preconditions: r
is dereferenceable.
如果 begin() + n
如果 n
大于容器的大小,则从 m
的某个值开始将不会满足此前提条件。 begin + 10;
之后你已经有了UB,剩下的代码就无关紧要了。
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
}
- N4659(2017 年 3 月 post-科纳工作 draft/C++17 DIS)
嗯,根据标准 §5/5.7:
,定义超出范围的迭代器是 UB
When an expression that has integral type is added to or subtracted
from a pointer, the result has the typeof the pointer operand. If the
pointer operand points to an element of an array object, and the array
islarge enough, the result points to an element offset from the
original element such that the difference ofthe subscripts of the
resulting and original array elements equals the integral expression.
In other words, if the expression points to the i-th
element of an
array object, the expressions (P)+N
(equivalently, N+(P))
and
(P)-N
(where N
has the valuen) point to, respectively, the
i+n-th
and i−n-th
elements of the arrayobject, provided they
exist. Moreover, if the expression P
points to the last element of
an array object,the expression (P)+1
points one past the last
element of the array object, and if the expressionQpointsone past the
last element of an array object, the expression (Q)-1
points to the
last element of the arrayobject. If both the pointer operand and the
result point to elements of the same array object, or one past the
last element of the array object, the evaluation shall not produce an
overflow; otherwise, the behavior is undefined
如果你打开 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
}
我在实际生产代码中发现了以下内容。 我怀疑它实际上有未定义的行为,但是,我在 cppreference 上找不到相关信息。您能否确认这是 UB 或有效代码以及为什么这是 UB/valid(最好引用标准)?
#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], Table 99 *:
difference_type m = n; if (m >= 0) while (m--) ++r; else while (m++) --r; return r;
而对于 ++r
,前提条件是 [input.iterators], Table 95 *:
Preconditions:
r
is dereferenceable.
如果 begin() + n
如果 n
大于容器的大小,则从 m
的某个值开始将不会满足此前提条件。 begin + 10;
之后你已经有了UB,剩下的代码就无关紧要了。
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
}
- N4659(2017 年 3 月 post-科纳工作 draft/C++17 DIS)
嗯,根据标准 §5/5.7:
,定义超出范围的迭代器是 UBWhen an expression that has integral type is added to or subtracted from a pointer, the result has the typeof the pointer operand. If the pointer operand points to an element of an array object, and the array islarge enough, the result points to an element offset from the original element such that the difference ofthe subscripts of the resulting and original array elements equals the integral expression. In other words, if the expression points to the
i-th
element of an array object, the expressions(P)+N
(equivalently,N+(P))
and(P)-N
(whereN
has the valuen) point to, respectively, thei+n-th
andi−n-th
elements of the arrayobject, provided they exist. Moreover, if the expressionP
points to the last element of an array object,the expression(P)+1
points one past the last element of the array object, and if the expressionQpointsone past the last element of an array object, the expression(Q)-1
points to the last element of the arrayobject. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined
如果你打开 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
}