Skip to content

Instantly share code, notes, and snippets.

@LegalizeAdulthood
Last active January 4, 2022 03:04
Show Gist options
  • Save LegalizeAdulthood/f5c2865d3ee9ede7a6da3afd0990d5f3 to your computer and use it in GitHub Desktop.
Save LegalizeAdulthood/f5c2865d3ee9ede7a6da3afd0990d5f3 to your computer and use it in GitHub Desktop.
A counterintuitive breaking change related to new comparisons C++17 vs. C++20
comp.lang.c++ #1074341 (1)
From: Andrey Tarasevich <andreytarasevich@hotmail.com>
[1] A counterintuitive breaking change related to new comparisons
Date: Mon Jan 03 10:48:42 MST 2022
Lines: 42
A colleague discovered that switching from `-stc=c++17` to `-std=c++20`
in their project resulted in a different behavior from some associative
containers. A bit of research allowed to narrow down the culprit to what
can be demonstrated by the following minimalist example
#include <iostream>
struct S
{
char c;
operator const char *() const { return &c; }
friend bool operator <(const S& lhs, const S& rhs)
{ return lhs.c < rhs.c; }
};
int main()
{
std::pair<int, S> p1{}, p2{};
std::cout << (p1 < p2) << (p2 < p1) << std::endl;
}
C++17 compilers output `00`. C++20 compilers output `01` (or, perhaps,
`10`).
The obvious guess is that comparisons for `std::pair` work differently
in C++20 after the introduction of `<=>`. And indeed, the `<=>` operator
for `std::pair` in this case ignores the user-defined `<` and instead
opts for raw pointer comparison through conversion to `const char *`.
This is a rather surprising and counterintuitive breaking change, to put
it mildly...
If we remove the user-defined conversion to `const char *`, C++20 will
use the user-defined `<` and also output `00`.
The above results were obtained with GCC. Clang 10 outputs `00` even in
`-std=c++20` mode, but Clang 11 and later output `01`.
--
Best regards,
Andrey Tarasevich
comp.lang.c++ #1074345
From: Andrey Tarasevich <andreytarasevich@hotmail.com>
[1] Re: A counterintuitive breaking change related to new comparisons
Date: Mon Jan 03 15:24:11 MST 2022
Lines: 75
On 1/3/2022 9:48 AM, Andrey Tarasevich wrote:
> A colleague discovered that switching from `-stc=c++17` to `-std=c++20`
> in their project resulted in a different behavior from some associative
> containers. A bit of research allowed to narrow down the culprit to what
> can be demonstrated by the following minimalist example
>
>   #include <iostream>
>
>   struct S
>   {
>     char c;
>
>     operator const char *() const { return &c; }
>
>     friend bool operator <(const S& lhs, const S& rhs)
>       { return lhs.c < rhs.c; }
>   };
>
>   int main()
>   {
>     std::pair<int, S> p1{}, p2{};
>     std::cout << (p1 < p2) << (p2 < p1) << std::endl;
>   }
>
> C++17 compilers output `00`. C++20 compilers output `01` (or, perhaps,
> `10`).
>
> The obvious guess is that comparisons for `std::pair` work differently
> in C++20 after the introduction of `<=>`. And indeed, the `<=>` operator
> for `std::pair` in this case ignores the user-defined `<` and instead
> opts for raw pointer comparison through conversion to `const char *`.
> This is a rather surprising and counterintuitive breaking change, to put
> it mildly...
>
> If we remove the user-defined conversion to `const char *`, C++20 will
> use the user-defined `<` and also output `00`.
>
> The above results were obtained with GCC. Clang 10 outputs `00` even in
> `-std=c++20` mode, but Clang 11 and later output `01`.
To take out of the picture the irrelevant matter of undefined pointer
comparison, here's another example
#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;
}
This program outputs `01` in C++17 mode (and earlier)
http://coliru.stacked-crooked.com/a/98f0b6b57e9caf45
but outputs `10` in C++20 mode
http://coliru.stacked-crooked.com/a/3f8c3e96cd39f596
In C++17 the user-defined comparison operator is used. In C++20
conversion to `int` and subsequent comparison of `int`s is used.
--
Best regards,
Andrey Tarasevich
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment