> I was specifically inspired by a performance bug due to a typo. This mistake is the “value param” vs “reference param” where your function copies a value instead of passing it by reference because an ampersand (&) was missing ... This simple typo is easy to miss the difference between `const Data& d` and `const Data d` isn't accurately characterized as "a typo" -- it's a semantically significant difference in inten…
The repercussions of missing an Ampersand in C++ and Rust
81–90 of 128 posts
Re: The repercussions of missing an Ampersand in C++ and Rust
#82Earlier quoted context omitted.
If the implications of a one char diff are this egregious that they’re considered obvious, maybe it should take less cognitive effort to spot this? CI and tooling are great, but would be far less necessary if it was more difficult to make this mistake in the first place.
What do you suggest? Some kind of std::const_reference ? Clang-tidy is enough in addition to the reviews.
Re: The repercussions of missing an Ampersand in C++ and Rust
#83Earlier quoted context omitted.
this is the defensive and correct C++ approach, anyways.
Isn't that just same old "skill issue", "No True C(++) programmer" refrain? If people could keep entirety of J.2 appendix in their mind at all time we would not have these issues. And if they had entirety of J appendix in mind all C code would be portable. Or if people just always ran -Wall -Wpedantic -Wall_for_real_this_time -fsanitize=thread,memory,address,leaks,prayers,hopes,dreams,eldritch_beings,elder_gods -fno-…
When your language is that unsafe and difficult to hold correctly, you have to make sure that you at least try your very best.
Re: The repercussions of missing an Ampersand in C++ and Rust
#84Another problem with C++ references is that they aren't really reference types, they are aliases, so they have wonky semantics and crazy nonsensical features like `const T&` doing lifetime extension
Re: The repercussions of missing an Ampersand in C++ and Rust
#85Earlier quoted context omitted.
> This means C++ is riddled with types that have unrelated "I'm empty" state Again, these cases are still rare. Most classes either don't require user-defined move operations, or they have some notion of emptiness or default state. > And if we don't want to allow None? Too bad, you can't express that in C++ That's actually a good example! Nitpick: you can express it in C++, just not without additional logic and some…
>you can express it in C++, just not without additional logic and some overhead :) How?
Re: The repercussions of missing an Ampersand in C++ and Rust
#86Earlier quoted context omitted.
Your socket class would have no default constructor? And you would never want to close the socket before the object's lifetime ends? Really?
With destructive moves, you can end an object's lifetime whenever you want.
Re: The repercussions of missing an Ampersand in C++ and Rust
#87Earlier quoted context omitted.
In practice, move operations typically just leave an empty object behind. The destructor already has to deal with that. And of course you can't call certain methods on an empty object. So in practice you don't need special logic except for the move operations themselves.
> The destructor already has to deal with that. That's partly true, partly circular. Because moves work this way, it's harder to make a class that doesn't have empty states, so I don't design my class to avoid empty states, so the destructor has to handle them.
Re: The repercussions of missing an Ampersand in C++ and Rust
#88Earlier quoted context omitted.
If the moves were destructive, I'd design it to have the default constructor call `::socket` and destructor call `::close`. And there wouldn't be any kind of "closed" state. Why would I want it?
Your socket class would have no default constructor? And you would never want to close the socket before the object's lifetime ends? Really?
Re: The repercussions of missing an Ampersand in C++ and Rust
#89Re: The repercussions of missing an Ampersand in C++ and Rust
#90Earlier quoted context omitted.
Your socket class would have no default constructor? And you would never want to close the socket before the object's lifetime ends? Really?
In this case, I would want the address family and protocol to be statically known, so it would have default constructor. But for example, a file might not have one, sure. As for closing before lifetime ends, why? I can just end lifetime. Wrap it in an optional if the type system can't figure it out like with a struct member.
And what's the underlying value of such a default constructed socket? I assume it would be -1 resp. INVALID_SOCKET, in which case the destructor would have to deal with it.
> Wrap it in an optional if the type system can't figure it out like with a struct member.
So you essentially must wrap it in an optional if you want to use it as a member variable. I find this rather pointless as sockets already have a well-defined value for empty state (-1 resp. INVALID_SOCKET). By wrapping it in a optional you are just wasting up to 8 bytes.
Sure, you can implement a socket class like that, but it's neither necessary nor idiomatic C++.