c++iteratorstd-span

Is comparing two spans pointing to the same container well defined?


Is the comparison of begin() and end() iterators of two std::spans 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.


Solution

  • Is comparison of begin() and end() iterators of two std::spans 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.

    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_views 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::spans that are equivalent, not just subspans.