Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #82705 > unrolled thread
| Started by | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| First post | 2022-01-03 09:48 -0800 |
| Last post | 2022-01-04 21:59 -0800 |
| Articles | 20 on this page of 21 — 8 participants |
Back to article view | Back to comp.lang.c++
A counterintuitive breaking change related to new comparisons Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-01-03 09:48 -0800
Re: A counterintuitive breaking change related to new comparisons Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2022-01-03 21:06 +0000
Re: A counterintuitive breaking change related to new comparisons Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-01-03 14:13 -0800
Re: A counterintuitive breaking change related to new comparisons Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2022-01-04 00:23 +0000
Re: A counterintuitive breaking change related to new comparisons Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-01-03 16:33 -0800
Re: A counterintuitive breaking change related to new comparisons Juha Nieminen <nospam@thanks.invalid> - 2022-01-04 08:27 +0000
Re: A counterintuitive breaking change related to new comparisons "Alf P. Steinbach" <alf.p.steinbach@gmail.com> - 2022-01-04 17:24 +0100
Re: A counterintuitive breaking change related to new comparisons Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-01-04 10:26 -0800
Re: A counterintuitive breaking change related to new comparisons Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2022-01-04 00:39 +0000
Re: A counterintuitive breaking change related to new comparisons Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-01-03 14:24 -0800
Re: A counterintuitive breaking change related to new comparisons "Alf P. Steinbach" <alf.p.steinbach@gmail.com> - 2022-01-03 23:55 +0100
Re: A counterintuitive breaking change related to new comparisons Manfred <noname@add.invalid> - 2022-01-04 00:56 +0100
Re: A counterintuitive breaking change related to new comparisons Manfred <noname@add.invalid> - 2022-01-04 00:59 +0100
Re: A counterintuitive breaking change related to new comparisons Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-01-03 16:59 -0800
Re: A counterintuitive breaking change related to new comparisons Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2022-01-04 01:27 +0000
Re: A counterintuitive breaking change related to new comparisons Öö Tiib <ootiib@hot.ee> - 2022-01-04 14:49 -0800
Re: A counterintuitive breaking change related to new comparisons Öö Tiib <ootiib@hot.ee> - 2022-01-04 15:16 -0800
Re: A counterintuitive breaking change related to new comparisons Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-01-05 09:06 -0800
Re: A counterintuitive breaking change related to new comparisons "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2022-01-03 18:51 -0800
Re: A counterintuitive breaking change related to new comparisons wij <wyniijj@gmail.com> - 2022-01-04 03:53 -0800
Re: A counterintuitive breaking change related to new comparisons wij <wyniijj@gmail.com> - 2022-01-04 21:59 -0800
Page 1 of 2 [1] 2 Next page →
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-01-03 09:48 -0800 |
| Subject | A counterintuitive breaking change related to new comparisons |
| Message-ID | <sqvcts$h94$1@dont-email.me> |
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
[toc] | [next] | [standalone]
| From | Chris Vine <chris@cvine--nospam--.freeserve.co.uk> |
|---|---|
| Date | 2022-01-03 21:06 +0000 |
| Message-ID | <20220103210610.ec3859f4d0b23edfc9d217c3@cvine--nospam--.freeserve.co.uk> |
| In reply to | #82705 |
On Mon, 3 Jan 2022 09:48:42 -0800
Andrey Tarasevich <andreytarasevich@hotmail.com> 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`.
As p1 and p2 are not within the same array or object, I believe
operator< has undefined behaviour so the compiler is within its rights
to behave this way (that was certainly the case in C++11). If you use
std::less it should work, as it provides an implementation defined
total order over pointers even where operator< does not.
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-01-03 14:13 -0800 |
| Message-ID | <sqvsdu$trp$1@dont-email.me> |
| In reply to | #82706 |
On 1/3/2022 1:06 PM, Chris Vine 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`.
>
> As p1 and p2 are not within the same array or object, I believe
> operator< has undefined behaviour so the compiler is within its rights
> to behave this way (that was certainly the case in C++11). If you use
> std::less it should work, as it provides an implementation defined
> total order over pointers even where operator< does not.
Um... The top-level `<` operation in `p1 < p2` is a user-defined
operator. It is not subject to any restrictions, like "within the same
array or object" or somesuch.
On the other hand, comparison between two `const char *` pointers,
obtained from `p1` and `p2` probably does have undefined behavior.
However, that's completely besides the point. The point is that in C++20
mode the compiler even opted to perform implicit conversion to `const
char *`. That is already surprising.
Before C++20 the conversion to `const char *` is not used and everything
is perfectly defined here. There's no "undefined behavior" of any kind
in C++17 semantics of this code.
--
Best regards,
Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | Chris Vine <chris@cvine--nospam--.freeserve.co.uk> |
|---|---|
| Date | 2022-01-04 00:23 +0000 |
| Message-ID | <20220104002324.1c51f5d1cced60e8e79a75ec@cvine--nospam--.freeserve.co.uk> |
| In reply to | #82707 |
On Mon, 3 Jan 2022 14:13:15 -0800
Andrey Tarasevich <andreytarasevich@hotmail.com> wrote:
> On 1/3/2022 1:06 PM, Chris Vine 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`.
> >
> > As p1 and p2 are not within the same array or object, I believe
> > operator< has undefined behaviour so the compiler is within its rights
> > to behave this way (that was certainly the case in C++11). If you use
> > std::less it should work, as it provides an implementation defined
> > total order over pointers even where operator< does not.
>
> Um... The top-level `<` operation in `p1 < p2` is a user-defined
> operator. It is not subject to any restrictions, like "within the same
> array or object" or somesuch.
>
> On the other hand, comparison between two `const char *` pointers,
> obtained from `p1` and `p2` probably does have undefined behavior. (
> However, that's completely besides the point. The point is that in C++20
> mode the compiler even opted to perform implicit conversion to `const
> char *`. That is already surprising.
>
> Before C++20 the conversion to `const char *` is not used and everything
> is perfectly defined here. There's no "undefined behavior" of any kind
> in C++17 semantics of this code.
Ah yes, I misread your code. You are not comparing pointers, you are
comparing indeterminate values. I would need to look it up but my
recollection is that reading (and so comparing) an uninitialized value
also comprises undefined behaviour, unless it is of unsigned char or
std::byte type. Here you seem to be comparing an uninitialized int and
char (via S:: operator <) type.
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-01-03 16:33 -0800 |
| Message-ID | <sr04ld$39j$1@dont-email.me> |
| In reply to | #82713 |
On 1/3/2022 4:23 PM, Chris Vine wrote: > Ah yes, I misread your code. You are not comparing pointers, you are > comparing indeterminate values. I would need to look it up but my > recollection is that reading (and so comparing) an uninitialized value > also comprises undefined behaviour, unless it is of unsigned char or > std::byte type. Here you seem to be comparing an uninitialized int and > char (via S:: operator <) type. No. That is incorrect. All values involved in these comparisons are perfectly initialized. Both `std::pair` objects are initialized with their default constructors, which in turn perform value-initialization of their members. In my example all `std::pair`s fields are guaranteed to be initialized to zero. Nothing indeterminate here. -- Best regards, Andrey Tarasevich .
[toc] | [prev] | [next] | [standalone]
| From | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| Date | 2022-01-04 08:27 +0000 |
| Message-ID | <sr10dd$mae$1@gioia.aioe.org> |
| In reply to | #82714 |
Andrey Tarasevich <andreytarasevich@hotmail.com> wrote: > Both `std::pair` objects are initialized with their default > constructors, which in turn perform value-initialization of their > members. In my example all `std::pair`s fields are guaranteed to be > initialized to zero. Nothing indeterminate here. Are you sure "default-initialization = zero-initialization" is guaranteed for POD objects? The default initialization of 'char' (as all the other primitive types) is "don't initialize". Is this different if the 'char' is a member of a POD object that has no explicit constructor?
[toc] | [prev] | [next] | [standalone]
| From | "Alf P. Steinbach" <alf.p.steinbach@gmail.com> |
|---|---|
| Date | 2022-01-04 17:24 +0100 |
| Message-ID | <sr1scd$ljh$1@dont-email.me> |
| In reply to | #82721 |
On 4 Jan 2022 09:27, Juha Nieminen wrote: > Andrey Tarasevich <andreytarasevich@hotmail.com> wrote: >> Both `std::pair` objects are initialized with their default >> constructors, which in turn perform value-initialization of their >> members. In my example all `std::pair`s fields are guaranteed to be >> initialized to zero. Nothing indeterminate here. > > Are you sure "default-initialization = zero-initialization" is guaranteed > for POD objects? > > The default initialization of 'char' (as all the other primitive types) is > "don't initialize". Is this different if the 'char' is a member of a POD > object that has no explicit constructor? Value initialization was about the only new feature in C++03, which was otherwise just Technical Corrigendum 1. It was invented by Andrew Koenig. The point of value initialization was to guarantee initialization of everything, if anything in an object was initialized (e.g., an object with a `std::string` and a `double` should not end up with the `string` initialized and the `double` indeterminate, as it could in C++98). For that purpose value initialization, used by the `std::pair` default constructor, ends up zero-initializing those things not otherwise initialized. - Alf
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-01-04 10:26 -0800 |
| Message-ID | <sr23g3$apj$1@dont-email.me> |
| In reply to | #82721 |
On 1/4/2022 12:27 AM, Juha Nieminen wrote:
> Andrey Tarasevich <andreytarasevich@hotmail.com> wrote:
>> Both `std::pair` objects are initialized with their default
>> constructors, which in turn perform value-initialization of their
>> members. In my example all `std::pair`s fields are guaranteed to be
>> initialized to zero. Nothing indeterminate here.
>
> Are you sure "default-initialization = zero-initialization" is guaranteed
> for POD objects?
>
> The default initialization of 'char' (as all the other primitive types) is
> "don't initialize". Is this different if the 'char' is a member of a POD
> object that has no explicit constructor?
`std::pair` is a not a POD type (if you insist on using outdated
terminology). It has a user-provided constructor.
In my example the top-level initialization is performed by the default
constructor of `std::pair`, which is guaranteed (by specification of
`std::pair`) to invoke *value-initialization* of both members.
Value-initialization of `char` (and other scalar types) is
zero-initialization.
Even if `std::pair` were a POD, the explicitly specified `{}`
initializer in my example would still trigger value-initialization of
the entire `std::pair`, which would in turn lead to value-initialization
of both members.
--
Best regards,
Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | Chris Vine <chris@cvine--nospam--.freeserve.co.uk> |
|---|---|
| Date | 2022-01-04 00:39 +0000 |
| Message-ID | <20220104003905.cac0d55d8098e541dd46e0a2@cvine--nospam--.freeserve.co.uk> |
| In reply to | #82713 |
On Tue, 4 Jan 2022 00:23:24 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> On Mon, 3 Jan 2022 14:13:15 -0800
> Andrey Tarasevich <andreytarasevich@hotmail.com> wrote:
> > On 1/3/2022 1:06 PM, Chris Vine 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`.
> > >
> > > As p1 and p2 are not within the same array or object, I believe
> > > operator< has undefined behaviour so the compiler is within its rights
> > > to behave this way (that was certainly the case in C++11). If you use
> > > std::less it should work, as it provides an implementation defined
> > > total order over pointers even where operator< does not.
> >
> > Um... The top-level `<` operation in `p1 < p2` is a user-defined
> > operator. It is not subject to any restrictions, like "within the same
> > array or object" or somesuch.
> >
> > On the other hand, comparison between two `const char *` pointers,
> > obtained from `p1` and `p2` probably does have undefined behavior. (
> > However, that's completely besides the point. The point is that in C++20
> > mode the compiler even opted to perform implicit conversion to `const
> > char *`. That is already surprising.
> >
> > Before C++20 the conversion to `const char *` is not used and everything
> > is perfectly defined here. There's no "undefined behavior" of any kind
> > in C++17 semantics of this code.
>
> Ah yes, I misread your code. You are not comparing pointers, you are
> comparing indeterminate values. I would need to look it up but my
> recollection is that reading (and so comparing) an uninitialized value
> also comprises undefined behaviour, unless it is of unsigned char or
> std::byte type. Here you seem to be comparing an uninitialized int and
> char (via S:: operator <) type.
Actually I think they should be initialized because of the brace
initializer. I don't use C++ very much any more so I may be wrong
about that; but if so there is surely something odd about this. It
looks like a bug.
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-01-03 14:24 -0800 |
| Message-ID | <sqvt2d$as4$1@dont-email.me> |
| In reply to | #82705 |
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
[toc] | [prev] | [next] | [standalone]
| From | "Alf P. Steinbach" <alf.p.steinbach@gmail.com> |
|---|---|
| Date | 2022-01-03 23:55 +0100 |
| Message-ID | <sqvutu$b15$1@dont-email.me> |
| In reply to | #82709 |
On 3 Jan 2022 23:24, Andrey Tarasevich wrote:
> 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.
>
I remember arguing for the superiority of memcmp/strcmp style comparison
functions, in old clc++m.
Boy was I stupid.
When I failed to think about how it could be fouled up by Microsoft-ish
thinking in the committee (or even that it could once be standardized).
- Alf
[toc] | [prev] | [next] | [standalone]
| From | Manfred <noname@add.invalid> |
|---|---|
| Date | 2022-01-04 00:56 +0100 |
| Message-ID | <sr02fm$17em$1@gioia.aioe.org> |
| In reply to | #82709 |
On 1/3/2022 11:24 PM, Andrey Tarasevich wrote:
> 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.
>
That's really odd indeed.
Are you sure that is is the C++20 standard that mandates this behavior?
I wouldn't be surprised if this were a plain bug in the implementation -
possibly related with the changes that you mention above - but still a bug.
In fact, gcc 11.2 with plain -std=c++20 gives '01':
https://www.godbolt.org/z/rEjfehdnY
[toc] | [prev] | [next] | [standalone]
| From | Manfred <noname@add.invalid> |
|---|---|
| Date | 2022-01-04 00:59 +0100 |
| Message-ID | <sr02lc$17em$2@gioia.aioe.org> |
| In reply to | #82711 |
On 1/4/2022 12:56 AM, Manfred wrote:
> On 1/3/2022 11:24 PM, Andrey Tarasevich wrote:
>> 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.
>>
>
> That's really odd indeed.
> Are you sure that is is the C++20 standard that mandates this behavior?
> I wouldn't be surprised if this were a plain bug in the implementation -
> possibly related with the changes that you mention above - but still a bug.
>
> In fact, gcc 11.2 with plain -std=c++20 gives '01':
>
> https://www.godbolt.org/z/rEjfehdnY
scratch that last bit (for some reason godbolt uses diferent comiler
selections fro compilation and program output)
https://www.godbolt.org/z/be1q7PErE
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-01-03 16:59 -0800 |
| Message-ID | <sr065g$brj$1@dont-email.me> |
| In reply to | #82711 |
On 1/3/2022 3:56 PM, Manfred wrote:
> On 1/3/2022 11:24 PM, Andrey Tarasevich wrote:
>> 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.
>>
>
> That's really odd indeed.
> Are you sure that is is the C++20 standard that mandates this behavior?
> I wouldn't be surprised if this were a plain bug in the implementation -
> possibly related with the changes that you mention above - but still a bug.
I'm not yet that good with the intricacies of the implicitly generated
comparators, but I see the dilemma the compiler is facing here. It is either
1. user-provided measly `<`-only comparison directly on `S`, or
2. implicit conversion that leads from `S` to `int` with strong
full-blown `<=>` comparison.
The compiler apparently opts for the latter.
I can see the attraction of the latter path, but the fact that it
quietly defeats a used-defined comparator looks scary. Granted, it
doesn't defeat it directly, since `std::pair`s comparator serves as an
intermediary, but still...
Once we declare the conversion operator as `explicit`, the phenomenon
expectedly disappears and C++20 falls back to the "traditional" behavior.
--
Best regards,
Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | Chris Vine <chris@cvine--nospam--.freeserve.co.uk> |
|---|---|
| Date | 2022-01-04 01:27 +0000 |
| Message-ID | <20220104012726.bf6d053ff90a58a662529632@cvine--nospam--.freeserve.co.uk> |
| In reply to | #82716 |
On Mon, 3 Jan 2022 16:59:25 -0800 Andrey Tarasevich <andreytarasevich@hotmail.com> wrote: [snip] > I'm not yet that good with the intricacies of the implicitly generated > comparators, but I see the dilemma the compiler is facing here. It is either > > 1. user-provided measly `<`-only comparison directly on `S`, or > > 2. implicit conversion that leads from `S` to `int` with strong > full-blown `<=>` comparison. > > The compiler apparently opts for the latter. > > I can see the attraction of the latter path, but the fact that it > quietly defeats a used-defined comparator looks scary. Granted, it > doesn't defeat it directly, since `std::pair`s comparator serves as an > intermediary, but still... > > Once we declare the conversion operator as `explicit`, the phenomenon > expectedly disappears and C++20 falls back to the "traditional" behavior. Frankly, this is unacceptable for an industry standard language such as C++. I was disappointed with some of the breaking changes in C++17 (one bizarre outcome of which was that std::uninitialized_copy, and the other std::unitialized_* functions, gave rise to undefined behaviour according to other parts of the same standard), but this one from C++20 to which you refer is I think worse because it causes breaking compiler behaviour in real life. C++ is not my first choice for new projects nowadays, although it still has some uses.
[toc] | [prev] | [next] | [standalone]
| From | Öö Tiib <ootiib@hot.ee> |
|---|---|
| Date | 2022-01-04 14:49 -0800 |
| Message-ID | <0f79324f-cd93-4829-a362-b88654547f1cn@googlegroups.com> |
| In reply to | #82716 |
On Tuesday, 4 January 2022 at 02:59:43 UTC+2, Andrey Tarasevich wrote:
> On 1/3/2022 3:56 PM, Manfred wrote:
> > On 1/3/2022 11:24 PM, Andrey Tarasevich wrote:
> >> 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.
> >>
> >
> > That's really odd indeed.
> > Are you sure that is is the C++20 standard that mandates this behavior?
> > I wouldn't be surprised if this were a plain bug in the implementation -
> > possibly related with the changes that you mention above - but still a bug.
> I'm not yet that good with the intricacies of the implicitly generated
> comparators, but I see the dilemma the compiler is facing here. It is either
>
> 1. user-provided measly `<`-only comparison directly on `S`, or
>
> 2. implicit conversion that leads from `S` to `int` with strong
> full-blown `<=>` comparison.
>
> The compiler apparently opts for the latter.
>
> I can see the attraction of the latter path, but the fact that it
> quietly defeats a used-defined comparator looks scary. Granted, it
> doesn't defeat it directly, since `std::pair`s comparator serves as an
> intermediary, but still...
>
> Once we declare the conversion operator as `explicit`, the phenomenon
> expectedly disappears and C++20 falls back to the "traditional" behavior.
I so far thought that <=> only breaks the philosophy of not
paying for what we don't use (implementation of < is often
cheaper than <=>, operator != is almost always cheaper).
Thanks for showing that it is far worse. :(
That it somehow manages to compose the <=> when you
make operator int() explicit is also odd ... I think all the
comparison operators of std::pair were removed
by C++20 and replaced with alien flying vessel operator.
[toc] | [prev] | [next] | [standalone]
| From | Öö Tiib <ootiib@hot.ee> |
|---|---|
| Date | 2022-01-04 15:16 -0800 |
| Message-ID | <4617859f-fc21-431b-86cf-7bb6362dc58cn@googlegroups.com> |
| In reply to | #82735 |
On Wednesday, 5 January 2022 at 00:49:55 UTC+2, Öö Tiib wrote:
> On Tuesday, 4 January 2022 at 02:59:43 UTC+2, Andrey Tarasevich wrote:
> > On 1/3/2022 3:56 PM, Manfred wrote:
> > > On 1/3/2022 11:24 PM, Andrey Tarasevich wrote:
> > >> 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.
> > >>
> > >
> > > That's really odd indeed.
> > > Are you sure that is is the C++20 standard that mandates this behavior?
> > > I wouldn't be surprised if this were a plain bug in the implementation -
> > > possibly related with the changes that you mention above - but still a bug.
> > I'm not yet that good with the intricacies of the implicitly generated
> > comparators, but I see the dilemma the compiler is facing here. It is either
> >
> > 1. user-provided measly `<`-only comparison directly on `S`, or
> >
> > 2. implicit conversion that leads from `S` to `int` with strong
> > full-blown `<=>` comparison.
> >
> > The compiler apparently opts for the latter.
> >
> > I can see the attraction of the latter path, but the fact that it
> > quietly defeats a used-defined comparator looks scary. Granted, it
> > doesn't defeat it directly, since `std::pair`s comparator serves as an
> > intermediary, but still...
> >
> > Once we declare the conversion operator as `explicit`, the phenomenon
> > expectedly disappears and C++20 falls back to the "traditional" behavior.
> I so far thought that <=> only breaks the philosophy of not
> paying for what we don't use (implementation of < is often
> cheaper than <=>, operator != is almost always cheaper).
> Thanks for showing that it is far worse. :(
>
> That it somehow manages to compose the <=> when you
> make operator int() explicit is also odd ... I think all the
> comparison operators of std::pair were removed
> by C++20 and replaced with alien flying vessel operator.
And oh ... not only std::pair ... all comparison operators of all
containers ... basically anyone comparing whatever
standard containers gets that pessimization together
with the risk of that breaking change manifesting.
Perhaps it is time to switch to boost from standard
containers until the madness hasn't yet spread to there:
<https://www.boost.org/doc/libs/1_78_0/doc/html/boost/container/vector.html>
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-01-05 09:06 -0800 |
| Message-ID | <sr4j6r$u78$1@dont-email.me> |
| In reply to | #82735 |
On 1/4/2022 2:49 PM, Öö Tiib wrote:
> On Tuesday, 4 January 2022 at 02:59:43 UTC+2, Andrey Tarasevich wrote:
>> I'm not yet that good with the intricacies of the implicitly generated
>> comparators, but I see the dilemma the compiler is facing here. It is either
>>
>> 1. user-provided measly `<`-only comparison directly on `S`, or
>>
>> 2. implicit conversion that leads from `S` to `int` with strong
>> full-blown `<=>` comparison.
>>
>> The compiler apparently opts for the latter.
>>
>> I can see the attraction of the latter path, but the fact that it
>> quietly defeats a used-defined comparator looks scary. Granted, it
>> doesn't defeat it directly, since `std::pair`s comparator serves as an
>> intermediary, but still...
>>
>> Once we declare the conversion operator as `explicit`, the phenomenon
>> expectedly disappears and C++20 falls back to the "traditional" behavior.
>
> I so far thought that <=> only breaks the philosophy of not
> paying for what we don't use (implementation of < is often
> cheaper than <=>, operator != is almost always cheaper).
> Thanks for showing that it is far worse. :(
>
> That it somehow manages to compose the <=> when you
> make operator int() explicit is also odd ... I think all the
> comparison operators of std::pair were removed
> by C++20 and replaced with alien flying vessel operator.
Potential performance pessimization for aggregate/container comparators
is indeed there, which can be seen from the following example
#include <utility>
#include <iostream>
struct S
{
int a;
S(int a) : a(a) {}
friend bool operator <(const S& lhs, const S& rhs)
{
bool b = lhs.a < rhs.a;
std::cout << "cmp " << lhs.a << " < " << rhs.a <<
" = " << b << std::endl;
return b;
}
};
int main()
{
std::pair<int, S> p1{ 0, 1 }, p2{ 0, 2 };
bool b = p1 < p2;
std::cout << "result = " << b << std::endl;
b = p2 < p1;
std::cout << "result = " << b << std::endl;
}
This case does not suffer from the original problem (no implicit
conversion present), meaning that in this case the comparison results
are consistent between C++17 and C++20. However, the paths that lead to
those results are still different.
When compiled as C++17 it outputs
cmp 1 < 2 = 1
result = 1
cmp 2 < 1 = 0
result = 0
When compiled as C++20 it outputs
cmp 1 < 2 = 1
result = 1
cmp 2 < 1 = 0
cmp 1 < 2 = 1
result = 0
C++20 still has to route the `std::pair` comparison through the `<=>`
operator. And ultimately it has to implement that operator based on the
available user-provided `<` comparison for `S`. In general case it takes
more calls to `<` to properly calculate the three-state result for
`<=>`, as evidenced by the above output. E.g. when "less" comparator
returns `false` an extra call is required to tell "equivalent" from
"greater".
If comparisons for `S` are "heavy", this might easily result in
significant pessimization.
So, even though the ultimate goal of three-state comparison support in
C++20 is to improve the performance of heavy comparisons, it does not
come for free: it still requires the user to take active steps to
provide efficient `<=>` comparators for their classes. Without it the
code might work correctly, but sub-optimally.
--
Best regards,
Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> |
|---|---|
| Date | 2022-01-03 18:51 -0800 |
| Message-ID | <sr0cmk$klm$2@dont-email.me> |
| In reply to | #82705 |
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`.
>
Bugger!
[toc] | [prev] | [next] | [standalone]
| From | wij <wyniijj@gmail.com> |
|---|---|
| Date | 2022-01-04 03:53 -0800 |
| Message-ID | <ff44479d-d9ee-4cc9-a079-56b008b9be8dn@googlegroups.com> |
| In reply to | #82705 |
On Tuesday, 4 January 2022 at 01:49:09 UTC+8, 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`.
>
> --
> Best regards,
> Andrey Tarasevich
// file t.cpp
#include <iostream>
struct S
{
char c;
operator const char *() const { return &c; }
friend bool operator <(const S& lhs, const S& rhs)
{
std::cout << 'c' ;
return lhs.c < rhs.c; }
};
int main()
{
std::pair<int, S> p1{}, p2{};
std::cout << (p1 < p2) << (p2 < p1) << std::endl;
}
----
[]$ g++ t.cpp -std=c++20
[]$ ./a.out
01
[]$ g++ t.cpp -std=c++17
[]$ ./a.out
c0c0
---
Problem was verified.
It looked to me an overloading issue with std::pair.
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.c++
csiph-web