在实现 std::vector 时,我是否必须拥有 end() 的 "end" 指针?
Do I have to own the "end" pointer for end() when implementing std::vector?
假设我有自己的 std::vector
实现,它提供 begin()
和 end()
函数用于迭代。可能 size()
==capacity()
和 end()
返回的指针不属于我。它是安全的还是我需要确保 size()
< capacity()
?
我知道这个难题可以通过使用自定义迭代器 类 来克服,但这里我说的是更简单的情况,当 begin()
只是 returns 指向缓冲区开始的指针, 和 end()
到 begin()+size()
.
标准禁止取消引用无效指针(并且标准详细描述了可能使指针无效的内容,例如指针对象的生命周期、生命周期内存块本身和指针的对齐方式)。
只要该指针的用户不取消引用它,并且在某些情况下只要该指针的用户不读取它(请参阅评论部分),就可以持有无效指针。
正如您自己建议的那样,end
就是 return begin() + size()
一个T*
值是有效如果它指向一个T
(它可能是一个数组的子对象) 或 如果它指向 T[]
的末尾之一。所有其他值都是 invalid,并且 using 这样的值是未定义的行为。
这意味着您可以比较(<
、==
等)有效 指针,但不是无效 指针。
A T*
值是 dereferenceable 如果它指向一个 T。取消引用一个 invalid 指针,或者一个-超出末端的指针是未定义的行为。这是一组与 valid 指针不同的值。
begin()+size()
会给你一个 valid 指针,它是 not dereferenceable.
您的第一个问题是目前无法手动滚动符合标准的 std 向量。无法为 K 个 T 创建一个缓冲区,其中前 N 个位于 T 数组中,然后在不重新分配前 N 个的情况下将其大小调整为 N+1 个 T。
这是因为包含 N(或 N+1)个相邻 T 的缓冲区不是 T 的数组,并且 T*
上的指针算法将无法正常工作(在standard) 仅在相邻 Ts 的缓冲区内,它与 T
s.
的真实数组一起工作的方式
这是因为标准限制了您可以在 T*
上进行指针运算的范围在 T
或之后;即使 T
存在于压缩结构或缓冲区中。这些限制限制了 "reachable" 对象的方式;如果您有一个包含 3 个 int
的结构,则 int*
上的任何算术都不能修改多个,因此可以假设某些缓存有效并且编译器不必每次都返回 RAM访问。
未解决的副作用是无法合法地实现 std 向量。这是标准中的一个缺陷,委员会知道它,我认为有人正在修复它(据我所知,它已在 c++20, I don't think I missed the fix in c++17 中修复)。
也就是说,您可以忽略所有这些,因为大多数编译器对在连续缓冲区中手动构建对象感到非常困惑,以至于他们利用假设的代码不会破坏您的手动向量。
无论如何,定义的指针值是数组中的指针值,数组末尾的指针值,为此目的,可以将单个对象视为大小为 1 的数组。如果取消引用,即使存在对象,最后一个指针也会显示 UB。该范围内的指针可以与 <
>=
等相互比较(但不能与指针 within/one-past 其他数组进行比较)。 None 会将 ==
与 null 进行比较,而 all 会将 !=
与 null 进行比较。 !=
和 ==
我相信与他们一起工作会如您所愿。
假设我有自己的 std::vector
实现,它提供 begin()
和 end()
函数用于迭代。可能 size()
==capacity()
和 end()
返回的指针不属于我。它是安全的还是我需要确保 size()
< capacity()
?
我知道这个难题可以通过使用自定义迭代器 类 来克服,但这里我说的是更简单的情况,当 begin()
只是 returns 指向缓冲区开始的指针, 和 end()
到 begin()+size()
.
标准禁止取消引用无效指针(并且标准详细描述了可能使指针无效的内容,例如指针对象的生命周期、生命周期内存块本身和指针的对齐方式)。
只要该指针的用户不取消引用它,并且在某些情况下只要该指针的用户不读取它(请参阅评论部分),就可以持有无效指针。
正如您自己建议的那样,end
就是 return begin() + size()
一个T*
值是有效如果它指向一个T
(它可能是一个数组的子对象) 或 如果它指向 T[]
的末尾之一。所有其他值都是 invalid,并且 using 这样的值是未定义的行为。
这意味着您可以比较(<
、==
等)有效 指针,但不是无效 指针。
A T*
值是 dereferenceable 如果它指向一个 T。取消引用一个 invalid 指针,或者一个-超出末端的指针是未定义的行为。这是一组与 valid 指针不同的值。
begin()+size()
会给你一个 valid 指针,它是 not dereferenceable.
您的第一个问题是目前无法手动滚动符合标准的 std 向量。无法为 K 个 T 创建一个缓冲区,其中前 N 个位于 T 数组中,然后在不重新分配前 N 个的情况下将其大小调整为 N+1 个 T。
这是因为包含 N(或 N+1)个相邻 T 的缓冲区不是 T 的数组,并且 T*
上的指针算法将无法正常工作(在standard) 仅在相邻 Ts 的缓冲区内,它与 T
s.
这是因为标准限制了您可以在 T*
上进行指针运算的范围在 T
或之后;即使 T
存在于压缩结构或缓冲区中。这些限制限制了 "reachable" 对象的方式;如果您有一个包含 3 个 int
的结构,则 int*
上的任何算术都不能修改多个,因此可以假设某些缓存有效并且编译器不必每次都返回 RAM访问。
未解决的副作用是无法合法地实现 std 向量。这是标准中的一个缺陷,委员会知道它,我认为有人正在修复它(据我所知,它已在 c++20, I don't think I missed the fix in c++17 中修复)。
也就是说,您可以忽略所有这些,因为大多数编译器对在连续缓冲区中手动构建对象感到非常困惑,以至于他们利用假设的代码不会破坏您的手动向量。
无论如何,定义的指针值是数组中的指针值,数组末尾的指针值,为此目的,可以将单个对象视为大小为 1 的数组。如果取消引用,即使存在对象,最后一个指针也会显示 UB。该范围内的指针可以与 <
>=
等相互比较(但不能与指针 within/one-past 其他数组进行比较)。 None 会将 ==
与 null 进行比较,而 all 会将 !=
与 null 进行比较。 !=
和 ==
我相信与他们一起工作会如您所愿。