Is the comparison of begin()
and end()
iterators of two std::span
s that point to the same memory well defined ?
#include <span>
#include <cassert>
int main() {
int arr[5]{1,2,3,4,5};
std::span s1{arr};
std::span s2{arr};
assert(s1.begin() == s2.begin());
assert(s1.end() == s2.end());
assert(s1.begin() + s1.size() == s2.end());
}
All asserts pass on all implementations of std::span
so far, but is there anything I may be missing that makes this a bug, e.g., UB?
For context, this can ariese if you have a class that tries to hide its internals with a span.
class some_class
{
public:
std::span<int> vec_view() { return vec; }
private:
std::vector<int> vec;
};
int main() {
some_class c;
std::for_each(c.vec_view().begin(), c.vec_view().end(), [](auto&){});
}
This is related to Does C++ allow comparison between std::span::iterators when one span is a subspan of the other?, but this question is not about a std::subspan
, and moreover the std::span
version also passes MSVC asserts, unlike the version with the std::subspan
.
Is comparison of
begin()
andend()
iterators of twostd::span
s that point to the same memory well-defined?
Who knows. std::span::iterator
models contiguous_iterator
([span.iterators]), and therefore, forward_iterator
.
[iterator.concept.forward] paragraph 2 applies:
The domain of
==
for forward iterators is that of iterators over the same underlying sequence. However, value-initialized iterators [...].
The C++ standard never defines what an "underlying sequence" is.
.begin()
and .end()
on, then std::span(...).begin() == std::span(...).begin()
is undefined. no matter what.operator*
on the iterators, then s1.begin() == s2.begin()
has to be true
.The first option is needlessly restrictive for non-owning ranges like std::string_view
and std::span
.
The second option may have unintended consequences, like being able to compare iterators from entirely different std::ranges::filter_view
s created with a function pointer as a predicate (same iterator type, same elements, but comparison is nonsensical).
Some language design decision will need to be made here, possibly case-by-case.
As you've already pointed out, there is an active LWG Issue 3989,
and until that is resolved, there won't be a definitive answer to this question, even for two std::span
s that are equivalent, not just subspans.