Last active
January 4, 2022 03:04
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
comp.lang.c++ #1074341 (1) | |
From: Andrey Tarasevich <[email protected]> | |
[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 <[email protected]> | |
[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