英文:
Where in the standard to I read or deduce that vector<T>::operator[] invokes UB for out-of-bound input?
问题
在cppreference上,我读到关于std::vector<T,Allocator>::operator[]
的以下内容:
> 通过这个运算符访问不存在的元素是未定义行为。
标准草案中是否包含类似的句子?或者,我应该从标准的哪个部分推断出这一点?
我猜如果标准没有提及,那就会造成未定义行为。但我也没有找到关于std::vector<T,Allocator>::at
的任何信息,所以...
英文:
On cppreference, I read the following about std::vector<T,Allocator>::operator[]
:
> Accessing a nonexistent element through this operator is undefined behavior.
Does the standard draft contain a similar sentence? Or, what part of the standard should I deduce this thing from?
I guess that if the standard says nothing about it, that would make it UB. But I haven't found anything about std::vector<T,Allocator>::at
either, so...
答案1
得分: 5
这部分内容的翻译如下:
-
它以一种非常迂回的方式进行了指定。
-
首先,成员未直接针对
vector
指定,而是针对通用的Sequence
。您可以在这里找到规范:http://eel.is/c++draft/sequence.reqmts。 -
首先,明显的部分是:
at
有以下条款:http://eel.is/c++draft/sequence.reqmts#123抛出: 如果
n >= a.size()
,则抛出out_of_range
异常。
这就是at
的行为来源。 -
至于
[]
,它说:返回:
*(a.begin() + n)
因此,效果与该表达式相同。我们需要遵循这一点,以找到未定义行为。为此,我们需要查看迭代器要求。 -
现在,C++20的要求几乎难以阅读,但C++17的要求仍然存在且适用。因此:
- http://eel.is/c++draft/iterator.requirements#random.access.iterators-1 表明
a + n
(对于某个迭代器a
和差异n
)意味着对a
的副本tmp
执行tmp += n
。 - 同一位置指出,
r += n
意味着重复应用++r
(或者如果n
为负数,则是--r
)。 - 在http://eel.is/c++draft/iterator.requirements#tab:inputiterator中,我们了解到
++r
和*a
都有先决条件,即r
或a
必须是可解引用的。请注意,在C++标准中违反先决条件会导致未定义行为。
- http://eel.is/c++draft/iterator.requirements#random.access.iterators-1 表明
这是非常复杂的部分。在这一点上,我们需要将vector::begin
和vector::end
的规范组合在一起,以了解重复递增v.begin()
将最终(即,在应用了v.size()
次后)生成v.end()
,它被指定为超出结尾迭代器。我找不到明确说明这些迭代器不可解引用的陈述,但是http://eel.is/c++draft/iterator.requirements#general-7规定标准库假定它们不能解引用,并且在vector的情况下确实如此。
因此,v[v.size()]
等同于*(v.begin() + v.size())
,等同于*v.end()
,这是未定义行为。
而v[v.size()+1]
等同于*(v.begin() + v.size() + 1)
,等同于*std::next(v.end())
,这是增加超出结尾迭代器,因此也是未定义行为。
英文:
It's specified in a very roundabout way.
First, the members aren't specified for vector
directly, but instead for the general Sequence
. You can find the specification here: http://eel.is/c++draft/sequence.reqmts.
First, the obvious part: at
has the clause
> Throws: out_of_range
if n >= a.size()
.
So this is where at
's behavior comes from.
As for []
, it says:
> Returns: *(a.begin() + n)
So the effect is the same as of that expression. We need to follow this to find the undefined behavior. For that we need to look at the iterator requirements.
Now the C++20 requirements are nigh-unreadable, but the C++17 requirements are still there and do apply. So:
- http://eel.is/c++draft/iterator.requirements#random.access.iterators-1 says that
a + n
(for some iteratora
and a differencen
) means doingtmp += n
for a copytmp
ofa
. - Same place says that
r += n
means repeated applications of++r
(or--r
ifn
is negative). - In http://eel.is/c++draft/iterator.requirements#tab:inputiterator we learn that
++r
and*a
both have the precondition thatr
ora
respectively are dereferenceable. Note that violating a precondition means undefined behavior in C++ standardese.
This is where it gets really complicated. At this point we need to piece together the specifications of vector::begin
and vector::end
to understand that repeated incrementing of v.begin()
will eventually (as in, after v.size()
applications) yield v.end()
, which is specified to be the past-the-end iterator. I cannot find the explicit statement that such iterators are not dereferenceable, but http://eel.is/c++draft/iterator.requirements#general-7 specifies that the standard library assumes they are not, and in vector's case this is true.
Therefore, v[v.size()]
is equivalent to *(v.begin() + v.size())
, which is equivalent to *v.end()
, which is undefined behavior.
And v[v.size()+1]
is equivalent to *(v.begin() + v.size() + 1)
, which is equivalent to *std::next(v.end())
, which is incrementing the past-the-end iterator and thus undefined behavior.
答案2
得分: 1
v[n]
等同于 *(v.begin() + n)
,根据 sequence.reqmts。
如果迭代器不在范围内,它就不可解引用,这意味着 v[n]
是未定义行为,根据 iterator.requirements.general:
对于迭代器 i,使得表达式 *i 被定义的值称为可解引用的。库永远不会假设超出末尾的值是可解引用的。
英文:
First note that v[n]
is equivalent to *(v.begin() + n)
as per sequence.reqmts.
Now, if the iterator is not within the range, it is not derefereceble meaning v[n]
is undefined behavior as per iterator.requirements.general:
> Values of an iterator i for which the expression *i is defined are called dereferenceable. The library never assumes that past-the-end values are dereferenceable.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论