Search code examples
c++c++17language-lawyerstandardsc++20

Exactly which parts of the standard require this breaking change involving operator <?


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;
}

Solution

  • 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.