DoZerg
DoZerg

Reputation: 323

How does std::basic_const_iterator support `it1 - it2`

Maybe it's a silly question, but I'm stuck on it now.

How does the following code work?

// g++ main.cpp -std=c++23

#include <iterator>

int main() {
    using It = std::basic_const_iterator<int*>;
    using CIt = std::basic_const_iterator<const int*>;

    It it1, it2;
    CIt cit;
    auto d1 = it1 - it2;  // ?
    auto d2 = it1 - cit;  // ?
}

From cppreference and GCC source, there is no operator - for two basic_const_iterators. The defined operator -s are:

Could someone explain how it1 - it2 and it1 - cit would work? Thanks!

Upvotes: 1

Views: 119

Answers (2)

user12002570
user12002570

Reputation: 1

Could someone explain how it1 - it2 would work?

This is equivalent to writing it1.operator-(it2) which uses the overloaded operator- verison 1.

Note that this selected overload is a member function as can be seen from the omitted friend specifier in the declaration of operator- in the standard c++.

template< std::sized_sentinel_for<Iter> S >
constexpr difference_type operator-( const S& s ) const; (1)  (since C++23)

When we apply this to it1.operator-(it2), one possible instantiation will be:

long std::basic_const_iterator<int*>::operator-<std::basic_const_iterator<int*> >(std::basic_const_iterator<int*> const&) const;

Here Iter = std::basic_const_iterator<int*> , and difference_type = long

Upvotes: 1

DoZerg
DoZerg

Reputation: 323

As many others have commented (thanks!), it1 - it2 calls it1.operator -(it2), or precisely:

template< std::sized_sentinel_for<Iter> S >
constexpr difference_type operator-( const S& s ) const;

cppreference

But still I wasn't satisfied because they didn't explain why. So I did a bit research myself.

Here is how it works:

(using It = basic_const_iterator<int *>)

  • it1 - it2 ==> it1.operator -(it2), because
    • sized_sentinel_for<It, int*> (ref) is true, because
      • It == int* ==> It::operator ==(int*) (ref), because
        • size_sentinel_for<int*, int*> is true.
      • int* == It is valid (why?)
      • It - int* ==> It::operator -(int*), because
        • sized_sentinel_for<int*, int*> is true.
      • int* - It ==> operator -(int*, It) (ref), because
        • sized_sentinel_for<int*, int*> is true.

The missing part of the puzzle was int* == It, until I tried the following code:

struct A {
    constexpr bool operator ==(int) const { return true; }
};

int main() {
    static_assert(1 == A{});
}

int == A can be synthesized from A::operator ==(int) in the new C++! (Seems the feature was introduced in C++20. Could someone point me to the Standard?)

So finally:

  • int* == It ==> It::operator ==(int*), because
    • Synthesized
    • size_sentinel_for<int*, int*> is true.

Update

Actually, operator <=> has the same synthesising, but not 2nd comparisons (!=, <, >, <=, >=):

#include <compare>

struct A {
    constexpr bool operator ==(int) const { return true; }
    constexpr auto operator <=>(int) const { return std::strong_ordering::equal; }
};

int main() {
    static_assert(1 == A{});
    static_assert((1 <=> A{}) == std::strong_ordering::equal);
}

Upvotes: 3

Related Questions