c++ - 对迭代器的钳制是否有效

标签 c++ stl iterator 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;
}
Code on Compiler Explorer
如您所见 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() + nm 的某个值开始,将不满足此前提条件如果 n大于容器的大小。后 begin + 10;你已经有了 UB,其余的代码是无关紧要的。
GCC 标准库 sanitizer(用 -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 月 Kona 后工作草案/C++17 DIS)
  • 关于c++ - 对迭代器的钳制是否有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62710870/

    相关文章:

    c++ - 将标准输出重定向到文件

    rust - 在 Rust 中定义采用迭代器而不消耗的函数的惯用方法是什么?

    c++ - 为什么visual studio会忽略项目文件中指定的tlb文件名

    c++ - std::for_each 优于 for 循环的优点

    c++ - 为什么 std::find 是这样实现的?

    c++ - 当第一个 vector 重新分配时,另一个 vector 中的 std::vectors 会重新分配吗?

    javascript - 在 ECMAScript 6 草案中,使用 StopIteration 异常来表示迭代结束的基本原理是什么?

    c++ - 是否有 .at() 等效于 multimap ?

    c++ - 我如何知道 IMAGE_THUNK_DATA 数组何时终止?

    c++ - SFML 的 std::bad_alloc 错误