2

A change was made in C++20, and I'm having a hard time finding where in the standard I can go look to learn that this change happened. Would someone please quote me the section of the standard that tells me this will happen?

I am aware of this question: Breaking change in std::tuple lexicographic comparison in C++20 with conversion operators?

but it does not answer the question about where I can find this out in the standard.

With both gcc 11.2 and clang this code prints out 01 (the expected result) with -std=c++17 and 10 with -std=c++20. Is this correct behavior? If it is, where in the standard should I look to find out why?

#include <iostream>

struct S {
    int a;

    S( int a ) : a( a ){ }

    operator
    int() const {
        return  a;
    }

    friend bool
    operator<( const S & lhs, const S & rhs ){
        return  lhs.a > rhs.a;
    }
};

int
main(){
    std::pair< int, S >  p1{ 0, 1 },  p2{ 0, 2 };
    std::cout <<  (p1 < p2)  <<  (p2 < p1)  << std::endl;
}
Sebastian
  • 1,834
  • 2
  • 10
  • 22
Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • In C++20, `pair`'s `<` goes through `<=>` if possible -- and it is possible for `S`. It's just that `(s1 <=> s2) < 0` gives a different answer than `s1 < s2`. – Barry Jan 10 '22 at 21:48
  • There might be a better dupe target for this, not sure. – Barry Jan 10 '22 at 21:49
  • @Barry - I'm very specifically interested in the standard. Unless there is a dupe that mentions the places in the standard I can go look to learn this, I'm not getting an answer to my question. – Omnifarious Jan 10 '22 at 21:55
  • @Barry - Thank you though for pointing me at that answer. It does help focus things a bit. Might it be a change to the documentation describing the standard library? – Omnifarious Jan 10 '22 at 22:03
  • My clang 13.0.0 (a *brew* install) with `-std=c++20` printed out `01`. – Eljay Jan 10 '22 at 22:20
  • 1
    @Eljay - I have a guess. Since this is a change to `::std::pair` and `::std::tuple` perhaps the available standard library isn't C++20 yet. Try it on compiler-explorer.com – Omnifarious Jan 11 '22 at 00:44

1 Answers1

3

In C++17, [pairs.spec] defined all the relational operators. For instance, operator< was specified as:

template <class T1, class T2>
constexpr bool operator<(const pair<T1, T2>& x, const pair<T1, T2>& y);

Returns: x.first < y.first || (!(y.first < x.first) && x.second < y.second).

In C++20, with the adoption of <=>, this looks a bit different. Also in [pairs.spec]:

template<class T1, class T2>
 constexpr common_comparison_category_t<synth-three-way-result<T1>,
                                         synth-three-way-result<T2>>
    operator<=>(const pair<T1, T2>& x, const pair<T1, T2>& y);

Effects: Equivalent to:

if (auto c = synth-three-way(x.first, y.first); c != 0) return c;
return synth-three-way(x.second, y.second);

Where synth-three-way(x, y) will do x <=> y if possible, otherwise do x < y and then y < x (see [expos.only.func]).

The issue here is that x <=> y for your type is actually valid, but does something different from x < y, so you get a different result.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Why is `x <=> y` valid? The `operator int()`? – Nathan Pierson Jan 10 '22 at 23:54
  • @NathanPierson Yes. As per usual with conversion functions. – Barry Jan 11 '22 at 00:24
  • 1
    @konsolas That's how conversion functions in C++ have always worked. [Since C++98](https://godbolt.org/z/44cK73rac). That's basically the point of writing conversion functions - to opt into the functionality that the other type provides. – Barry Jan 11 '22 at 00:26