After switching to C++20 I found that some of our tests failed. The output of the following code is different between C++17 and C++20 modes:
https://godbolt.org/z/hx4a98T13
class MyString
{
public:
operator char const *() const
{
std::printf("convert ");
return nullptr;
}
bool operator<(MyString const & other) const
{
std::printf("MyString::operator< ");
return 1;
}
};
int main()
{
MyString s1;
MyString s2;
std::printf("s1 < s2 = %d\n", s1 < s2);
std::pair<MyString, MyString> pair1;
std::pair<MyString, MyString> pair2;
std::printf("pair1 < pair2 = %d\n", pair1 < pair2);
}
/// C++17
MyString::operator< s1 < s2 = 1
MyString::operator< pair1 < pair2 = 1
/// C++20
MyString::operator< s1 < s2 = 1
convert convert convert convert pair1 < pair2 = 0
It seems that std::pair::operator<=>
prefers synthesized operator<
over user-provided. Why is that so?
PS: I do know that making conversion operator explicit solves the issue, but I would really like to know what causes this behavior.
This is a (presumably accidental) breaking change in C++20.
In C++20, ordering comparisons on std::pair
use operator<=>
, which in turn will use operator<=>
(if possible) on its components.
The only available operator<=>
on your MyString
type is on the converted char const *
, so that becomes the chosen comparison.
You can fix this by having MyString
implement operator<=>
instead of operator<
.
Or you can make operator char const *()
explicit
, which will provide no operator<=>
, causing the the three-way comparison to be synthesized from your operator<
.