←back to thread

In Defense of C++

(dayvster.com)
185 points todsacerdoti | 2 comments | | HN request time: 0s | source
Show context
loeg ◴[] No.45268662[source]
> in C++, you can write perfectly fine code without ever needing to worry about the more complex features of the language. You can write simple, readable, and maintainable code in C++ without ever needing to use templates, operator overloading, or any of the other more advanced features of the language.

This... doesn't really hold water. You have to learn about what the insane move semantics are (and the syntax for move ctors/operators) to do fairly basic things with the language. Overloaded operators like operator*() and operator<<() are widely used in the standard library so you're forced to understand what craziness they're doing under the hood. Basic standard library datatypes like std::vector use templates, so you're debugging template instantiation issues whether you write your own templated code or not.

replies(7): >>45268759 #>>45268766 #>>45269024 #>>45272274 #>>45272736 #>>45274243 #>>45274785 #
butterisgood ◴[] No.45268759[source]
Overloaded operators were a terrible mistake in every programming language I've encountered them in. (Yes, sorry Haskell, you too!)

I don't think move semantics are really that bad personally, and some languages move by default (isn't that Rust's whole thing?).

What I don't like is the implicit ambiguous nature of "What does this line of code mean out of context" in C++. Good luck!

I have hope for C++front/Cpp2. https://github.com/hsutter/cppfront

(oh and I think you can write a whole book on the different ways to initialize variables in C++).

The result is you might be able to use C++ to write something new, and stick to a style that's readable... to you! But it might not make everyone else who "knows C++" instantly able to work on your code.

replies(5): >>45268857 #>>45268992 #>>45269102 #>>45271097 #>>45275305 #
tialaramex ◴[] No.45275305[source]
So, what programmers wanted (yes, already before C++ got this) was what are called "destructive move semantics".

These assignment semantics work how real life works. If I give you this Rubik's Cube now you have the Rubik's Cube and I do not have it any more. This unlocks important optimisations for non-trivial objects which have associated resources, if I can give you a Rubik's Cube then we don't need to clone mine, give you the clone and then destroy my original which is potentially much more work.

C++ 98 didn't have such semantics, and it had this property called RAII which means when a local variable leaves scope we destroy any values in that variable. So if I have a block of code which makes a local Rubik's Cube and then the block ends the Rubik's Cube is destroyed, I wrote no code to do that it just happens.

Thus for compatibility, C++ got this terrible "C++ move" where when I give you a Rubik's Cube, I also make a new hollow Rubik's Cube which exists just to say "I'm not really a Rubik's Cube, sorry, that's gone" and this way, when the local variable goes out of scope the destruction code says "Oh, it's not really a Rubik's Cube, no need to do more work".

Yes, there is a whole book about initialization in C++: https://www.cppstories.com/2023/init-story-print/

For trivial objects, moving is not an improvement, the CPU can do less work if we just copy the object, and it may be easier to write code which doesn't act as though they were moved when in fact they were not - this is obviously true for say an integer, and hopefully you can see it will work out better for say an IPv6 address, but it's often better for even larger objects in some cases. Rust has a Copy marker trait to say "No, we don't need to move this type".

replies(1): >>45275901 #
1. AnimalMuppet ◴[] No.45275901[source]
In particular, move is important if there is something like a unique_ptr. To make a copy, I have to make a deep copy of whatever the unique_ptr points to, which could be very expensive. To do a move, I just copy the bits of the unique_ptr, but now the original object can't be the one that owns what's pointed to.
replies(1): >>45280509 #
2. tialaramex ◴[] No.45280509[source]
Sure. Notice std::unique_ptr<T> is roughly equivalent to Rust's Option<Box<T>>

The C++ "move" is basically Rust's core::mem::take - we don't just move the T from inside our box, we have to also replace it, in this case with the default, None, and in C++ our std::unique_ptr now has no object inside it.

But while Rust can carefully move things which don't have a default, C++ has to have some "hollow" moved-from state because it doesn't have destructive move.