C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::span<T>::iterator convertible to std::span<T const>::iterator

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Wed, 25 Oct 2023 10:37:05 -0400
On Wed, Oct 25, 2023 at 10:04 AM Hewill Kang via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> But it would be nice
>> to have automatic conversion between std::span<T>::iterator convertible
>> to std::span<T const>::iterator.
>
>
> In C++23, you can just use span<T>::const_iterator.
>

I assume OP's code is something like this:

    void test(std::span<int> s, std::span<const int> cs) {
        if (s.begin() == cs.begin()) { ... } // non-portable
        if (s.cbegin() == cs.begin()) { ... } // still non-portable, IIUC
    }

OP, IMHO LWG 3989 <https://cplusplus.github.io/LWG/issue3989> is closely
related to your problem. The question there is whether we can ask e.g. `if
(s.end() + 1 == cs.begin())`. At least MSVC and libc++ say "no, you cannot
portably ask that at all; that's a bounds-check violation."
In OP's case also, I think there's a plausible argument to be made that `s`
and `cs` are different span objects, and iterators into one span shouldn't
be allowed to compare with iterators into the other, any more than you're
allowed to do that with iterators-into-vectors or
iterators-into-std::arrays.

The portable solution to both problems is to drop down into
raw-pointer-land:

    void test(std::span<int> s, std::span<const int> cs) {
        if (s.data() == cs.data()) { ... } // OK
        if (std::to_address(s.begin()) == std::to_address(cs.begin())) {
... } // OK
    }

I tend to agree with OP that this is a bad situation. I wish span iterators
were just pointers. But I don't think that's ever likely to happen (in the
mainstream library vendors, at least), so you've just got to work around it
in the (hopefully rare) cases where it matters.

Related reading:
https://quuxplusone.github.io/blog/2022/03/03/why-isnt-vector-iterator-just-t-star/

–Arthur

Received on 2023-10-25 14:37:19