admin 管理员组文章数量: 1086019
While browsing cppreference to see if there is any container (or adapter or view) whose end
can be dereferenced, I stumbled upon std::span::end
. The article has the usual note saying:
Returns an iterator to the element following the last element of the span.
This element acts as a placeholder; attempting to access it results in undefined behavior.
However, std::span
does not own its elements. std::span::end
does not necessarily refer to one past the last element of the actual container.
Does this code invoke undefined behavior?
#include <vector>
#include <span>
#include <iostream>
int main() {
std::vector<int> v{1,2,3,4,5};
std::span<int> s{v.begin(),v.begin()+2};
std::cout << *(s.end());
}
With a naive implementation it would work just fine. Though, an implementation might make assumptions (eg "end
is never dereferenced") that would break the above code. Hence, the question is whether the standard formally declares this as UB (or if its a mistake in the cppreference article).
While browsing cppreference to see if there is any container (or adapter or view) whose end
can be dereferenced, I stumbled upon std::span::end
. The article has the usual note saying:
Returns an iterator to the element following the last element of the span.
This element acts as a placeholder; attempting to access it results in undefined behavior.
However, std::span
does not own its elements. std::span::end
does not necessarily refer to one past the last element of the actual container.
Does this code invoke undefined behavior?
#include <vector>
#include <span>
#include <iostream>
int main() {
std::vector<int> v{1,2,3,4,5};
std::span<int> s{v.begin(),v.begin()+2};
std::cout << *(s.end());
}
With a naive implementation it would work just fine. Though, an implementation might make assumptions (eg "end
is never dereferenced") that would break the above code. Hence, the question is whether the standard formally declares this as UB (or if its a mistake in the cppreference article).
2 Answers
Reset to default 6I think it is implementation-defined as to whether dereferencing std::span::end()
is always unsafe or sometimes defined behaviour. Pedantically that isn't the same as undefined behaviour, but it is very close.
std::span<T>::iterator
is an implementation defined type that satisfies std::contiguous_iterator
etc, it need not be exactly T*
.
An implementation could choose to check on dereference that you are in range. If it does, then your deference could throw (or whatever).
Instead, if the implementation chooses to use T*
(or a class that wraps it and does no checking) as std::span<T>::iterator
, then your dereference is valid, as it's a pointer value that we know is dereferenceable.
In contrast, if your example used std::subrange<std::vector<int>::iterator, std::vector<int>::iterator> s{v.begin(),v.begin()+2};
, then it would be defined behaviour, as std::subrange::end()
returns the sentinel value, which is a dereferenceable std::vector<int>::iterator
.
I believe the answer is yes. It is undefined. In span.iterators#4, the iterator is specified to return the past-the-end value.
constexpr iterator end() const noexcept;
Returns: An iterator which is the past-the-end value.
In iterator.requirements.general#7, there is a blanket statement for "past-the-end value" (emphasis mine):
Just as a regular pointer to an array guarantees that there is a pointer value pointing past the last element of the array, so for any iterator type there is an iterator value that points past the last element of a corresponding sequence. Such a value is called a past-the-end value. 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.
Note that the blacket statement uses very strong language and doesn't even go with the usual "unless otherwise specified" that is commonly used in other parts of the standard. So I believe it is clear that it is not allowed to dereference std::span::end()
.
The example (given in the answer by @Caleth) of *std::subrange {v.begin(),v.begin()+2}.end()
being well-defined does not contradict the above claim because the end()
is not specified in the standard to return a "past-the-end value", but that does give an answer to your original question (inspiration) about "whether there is any container (or adapter or view) whose end
can be dereferenced".
本文标签: cIs dereferencing stdspanend always undefinedStack Overflow
版权声明:本文标题:c++ - Is dereferencing std::span::end always undefined? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://roclinux.cn/p/1744100330a2533469.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
span::iterator
– Jarod42 Commented Mar 27 at 9:31std::abort
if any attempt was made to dereference it at end (or anywhere outside of it's designated valid domain), that would still comply with the standard. (Any undefined behavior becomingstd::abort
in my fantasy paranoia standard C++ library.) – Eljay Commented Mar 27 at 11:06