Monday, January 3, 2022

Digest for comp.lang.c++@googlegroups.com - 14 updates in 2 topics

Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 03 09:48AM -0800

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
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jan 03 09:06PM

On Mon, 3 Jan 2022 09:48:42 -0800
> 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.
Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 03 02:13PM -0800

On 1/3/2022 1:06 PM, Chris Vine wrote:
> 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
Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 03 02:24PM -0800

On 1/3/2022 9:48 AM, Andrey Tarasevich wrote:
> 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
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Jan 03 11:55PM +0100

On 3 Jan 2022 23:24, Andrey Tarasevich wrote:
> 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
Juha Nieminen <nospam@thanks.invalid>: Jan 03 06:21AM

> {
> static constexpr float inf = std::numeric_limits<float>::infinity();
> }
 
I don't think a constexpr variable needs to be 'static'.
red floyd <no.spam.here@its.invalid>: Jan 03 12:11AM -0800

On 1/2/2022 10:21 PM, Juha Nieminen wrote:
>> static constexpr float inf = std::numeric_limits<float>::infinity();
>> }
 
> I don't think a constexpr variable needs to be 'static'.
 
Yeah, don't they have internal linkage by default, like const does?
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Jan 03 09:28AM +0100

On 3 Jan 2022 09:11, red floyd wrote:
>>> }
 
>> I don't think a constexpr variable needs to be 'static'.
 
> Yeah, don't they have internal linkage by default, like const does?
 
Yes, `constexpr` implies (drags in) `inline` and `const`.
 
Still, in a class definition `static` means something else than above,
and can't be omitted for a `constexpr` data member.
 
That's unreasonable to me: the language /requiring/ you to write a
meaningless filler word, a word that can't be omitted but carries no
extra meaning and doesn't help the compiler's parsing. As I see it
`constexpr` for data should also have implied static storage duration.
It's gut-wrenching that it does not.
 
- Alf
Juha Nieminen <nospam@thanks.invalid>: Jan 03 08:36AM

> extra meaning and doesn't help the compiler's parsing. As I see it
> `constexpr` for data should also have implied static storage duration.
> It's gut-wrenching that it does not.
 
I haven't tried it, but couldn't a (non-static) constexpr member
variable be usable in classes that are intended for constexpr
evaluation only?
 
Or is it semantically just not possible to have a non-static
constexpr member variable?
Muttley@dastardlyhq.com: Jan 03 09:24AM

On Mon, 3 Jan 2022 09:28:25 +0100
 
>That's unreasonable to me: the language /requiring/ you to write a
>meaningless filler word, a word that can't be omitted but carries no
>extra meaning and doesn't help the compiler's parsing. As I see it
 
In a similar vein, why does C++ still insist on class static values being set
outside of the class? eg:
 
struct myclass
{
static int i;
};
 
int myclass::i = 123;
 
Why on earth can't I just do:
 
struct myclass
{
static int i = 123;
};
 
in the same way that const members are initialised? I've never seen a good
reason why it must be this way and it makes for messy header files.
David Brown <david.brown@hesbynett.no>: Jan 03 05:14PM +0100

> };
 
> in the same way that const members are initialised? I've never seen a good
> reason why it must be this way and it makes for messy header files.
 
struct myclass
{
inline static int i = 123;
};
 
This has been valid since C++17, along with inline variables. (Why do
you need "inline" here? Good question, and I hope someone else can
geive a good answer!)
 
<https://en.cppreference.com/w/cpp/language/static#Static_data_members>
Muttley@dastardlyhq.com: Jan 03 04:22PM

On Mon, 3 Jan 2022 17:14:51 +0100
 
>This has been valid since C++17, along with inline variables. (Why do
>you need "inline" here? Good question, and I hope someone else can
>geive a good answer!)
 
Thanks for that, didn't know. Hope the inline doesn't throw up wierd
optimisation issues. Why its taken over 3 decades to do this simple obvious
change when they've been busy throwing in a lot of syntatic nonsense no one
asked for (user literals being the best example IMO) instead is also a good
question.
scott@slp53.sl.home (Scott Lurndal): Jan 03 05:40PM


>This has been valid since C++17, along with inline variables. (Why do
>you need "inline" here? Good question, and I hope someone else can
>give a good answer!)
 
Probably for backward compatability. Note that historically C++ was
translated to C then compiled (e.g. cfront). Trying to do the static
initialization in the class definition would result in linker errors
due to duplicate definitions of the static variable when the class
definition is include by multiple compilation units. Hence the requirement
to declare it in a single compilation unit.
Manfred <noname@add.invalid>: Jan 03 11:14PM +0100

On 1/3/2022 9:11 AM, red floyd wrote:
>>> }
 
>> I don't think a constexpr variable needs to be 'static'.
 
> Yeah, don't they have internal linkage by default, like const does?
 
That's true, but I still like to be explicit, since 'static' and 'const'
mean different things.
 
The irony is that this is probably one of those examples where C beats
C++ - good ol' HUGE_VALF or INFINITE would raise no question at all!
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: