对迭代器的钳制是否有效

JVA*_*pen 5 c++ iterator stl language-lawyer

我在实际的生产代码中发现了以下内容。我怀疑它实际上有未定义的行为,但是,我在 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;
}
Run Code Online (Sandbox Code Playgroud)

编译器资源管理器上的代码

如您所见,begin + 10将创建一个超出std::vector. 但是,没有使用该迭代器,因为它使用std::min.

Evg*_*Evg 9

operator+(n)对于随机访问迭代器,的操作语义是这个[random.access.iterators],表 99 *:

difference_­type m = n;
if (m >= 0)
    while (m--)
        ++r;
else
    while (m++)
        --r;
return r;
Run Code Online (Sandbox Code Playgroud)

而对于++r前置条件是[input.iterators],表95 *:

前提条件:r可解引用。

有了begin() + n这个前提不会从某些价值开始得到满足m,如果n是大于容器的尺寸更大。后begin + 10;你已经有了 UB 之后,剩下的代码就无关紧要了。

GCC 标准库 sanitizer (compile with -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
    }
Run Code Online (Sandbox Code Playgroud)
  • N4659(2017 年 3 月 Kona 后工作草案/C++17 DIS)


小智 2

好吧,根据标准\xc2\xa75/5.7定义一个超出范围的迭代器是 UB :

\n
\n

当具有整型类型的表达式与指针相加或相减时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向相对于原始元素的元素偏移量,使得结果数组元素和原始数组元素的下标之差等于积分表达式。\n换句话说,如果表达式指向i-th数组对象的元素,则表达式(P)+N(等效地,N+(P))和\n (P)-N(其中N有值 n)分别指向数组对象的\ni+n-th和元素,前提是它们i\xe2\x88\x92n-th\n存在。此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向数组对象的最后一个元素,如果表达式指向数组对象的最后一个元素,则表达式指向数组对象的最后一个(Q)-1元素到数组对象的\n最后一个元素。如果指针操作数和\n结果都指向同一个数组对象的元素,或者超过数组对象的\n最后一个元素,则求值不会\n产生溢出;否则,行为未定义

\n
\n

如果你打开 gcc 的迭代器调试,你可以验证这一点

\n
# g++ main.cpp -D_GLIBCXX_DEBUG -o main\n# ./main\nC:/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:\nError: attempt to advance a dereferenceable (start-of-sequence) iterator 10\nsteps, which falls outside its valid range.\n\nObjects involved in the operation:\n    iterator @ 0x0061FE3C {\n      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);\n      state = dereferenceable (start-of-sequence);\n      references sequence with type \'std::__debug::vector<int, std::allocator<int> >\' @ 0x0061FE50\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

  • 另一个答案更好,因为它明确(未)定义迭代器的行为,而您的答案只是提到指针并假设(当然,相当公平)它们代表迭代器。然而,这不一定是真的,我认为也不足以最终回答有关迭代器的具体问题。 (2认同)