4 ms·
Would it be fair to say that things are so complicated (compared to all other programming languages I've used in my professional life), because C++ pre-move sem
by criemen 11mo ago
Would it be fair to say that things are so complicated (compared to all other programming languages I've used in my professional life), because C++ pre-move semantics defaulted to deep copy semantics? It seems to be set apart in that choice from many other languages.
- htfy96 11mo agoCorrect. As someone who maintain a 16-year-old C++ code base with new features added every day, The status quo is the best incremental improvement over deep copy semantics. There are better choices if everything is built from scratch, but changing wheels from a running car isn't easy.
- einpoklum 11mo agoSorry to be the nitpicker here, but - the best incremental improvement would probably have seen move-destruction instead of just moves, which keep the source object alive and force the allowance for a valid 'empty' or 'dummy' state. See also: * https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm#Alternative https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n13... move designs * https://www.foonathan.net/2017/09/destructive-move/ https://www.foonathan.net/2017/09/destructive-move/
- kccqzy 11mo agoDeep copy is pedagogically and semantically the right choice for any mutable containers. You either make containers immutable or copies deep. Otherwise it's just an invitation for subtle bugs.
- criemen 11mo agoI'm not sure about that - every time I copy an object I have to think through what happens, no matter the default semantics. C++ makes the deep copy case easier than other programming languages without top-level built-in support.
- hgomersall 11mo agoNo, it should move properly when passing by value (as in, essentially the rust move semantics). If you want a copy, that should be explicit.
- hyghjiyhu 11mo agoMoving by default would be too much of a footgun without a borrow checker imo.
- hgomersall 11mo agoI think using a language without a borrow checker is already a massive footgun (albeit less of one for GC/RC languages). More sensible move semantics would still be a big ergonomic improvement.
- kccqzy 11mo agoI think the language allowing use-after-move is honestly a smaller footgun than allowing using uninitialized memory or use-after-free. C++ already has the latter.
- cjfd 11mo agoIt certainly makes things easier. But it also makes some things very, very, very inefficient. I want a list with millions/billions of elements. I want to regularly change one of the elements somewhere in the middle. Good luck with the copying.
- vlovich123 11mo agoThen explain that const isn’t deep and a const container can end up mutating state? Pretending like c++ has a consistent philosophy is amusing and pretending this happened because of pedagogy is amusing. It happened because in c assignment is a copy and c++ inherited this regardless of how dumb it is as a default for containers.
- kccqzy 11mo agoIn C++ regular types have the property that const is deep. If you have a const std::vector<int> the you can't modify any of the integers contained in this container. Naturally for flexibility reasons not all types are regular, pointers being the prominent exception, and things like std::string_view being modern examples of non-regular types.
- vlovich123 11mo agoI feel like you could benefit from watching Scott Meyers about the silliness in C++ if you feel like there’s a consistent and logical feel to the language. A lot of this is c++-isms masquerading sensible ideas through official terms (regular and non-regular types)
- kccqzy 11mo agoOh I certainly do not feel like there's consistency in the language. The C++ language is such that everyone picks a subset that makes sense to them and is relatively consistent. Regular and non-regular types are however a basic idea that transcends languages. I can write a snippet in Python too: import copy from some.random.module.on.pypi import foo a = foo() b = copy.copy(a) assert a == b If that assertion fails it implies the type isn't regular.
- vlovich123 11mo agoCan you point me to terminology other than the c++ spec (preferably literature) that defines regular types? I can only find references to this concept in c++ land.
- HarHarVeryFunny 11mo agoC++ supports both pass-by-value and pass-by-reference parameters. Pass-by-value means making a copy (a deep copy if it's a deep type), but you could always choose to optimize by passing large parameters by reference instead, and this is common practice. The real value of std::move is cases where you to HAVE to (effectively) make a deep copy, but still want to avoid the inefficiency. std::move supports this because moving means "stealing" the value from one variable and giving it to another. A common use case is move constructors for objects where you need to initialize the object's member variables, and can just move values passed by the caller rather than copying them. Another important use case for std::move is returning values from functions, where the compiler will automatically use move rather than copy if available, allowing you to define functions returning large/complex return types without having to worry about the efficiency.
- tubs 11mo ago(More so since c++17) std::move should not be used for returns because this pessimises optimisations.
- w10-1 11mo ago1990's C practice was to document whether initialization values were copied or adopted. I'm curious why the concept became "move" rather than "adopt", since move gives the parameter/data agency instead of giving agency to the consuming component.
- HarHarVeryFunny 11mo agoI'm not sure how any notion of ownership and adoption in C would carry over to C++, since one of the problems with C is that it didn't define ownership. If you have a pointer value in C it means nothing about ownership. Given the limits of C, "adopting" a pointer variable would just mean that the foster-parent now had a copy of the pointer, and was considered owner of it, now responsible for freeing it too presumably. It's basically a move, except the source can now mess things up by still using the adopted value after the adoption.
- _zagj 11mo ago> defaulted to deep copy semantics It defaulted to pass-by-value, with shallow copy semantics, as opposed to pass by reference.
- HarHarVeryFunny 11mo agoNo, pass-by-value means copying, which means whatever the copy constructor of the type implements, which for all standard types means a deep copy. You COULD define you own type where the copy constructor did something other than deep copy (i.e. something other than copy!), just as you could choose to take a hand gun and shoot yourself in the foot.
- Maxatar 11mo agoYou seem to misunderstand the meaning of deep versus shallow copy. This distinction has to do with how references/pointers get copied. In C++ the compiler generated copy constructors are shallow, not deep, meaning that if you want a copy to reconstruct the object being pointed to, you can not use the default copy constructor but need to supply your own explicit copy constructor to do so.
- HarHarVeryFunny 11mo agoNo - you are confusing C and C++. In C a structure passed by value will just be shallow-copied. In C++ a structure/class passed by value will by copied using whatever copy constructor is defined by the structure type. If there is no explicitly defined copy constructor, then a default copy constructor will be used that does member-wise copy using the member's copy constructors. So, unless you have chosen to shoot yourself in the foot by defining a class who's copy constructor doesn't do a deep copy, then C++ pass-by-value will indeed do a deep copy. The case of a structure with a pointer member (e.g. maybe you defined your own string class) is certainly one where you would need to define an appropriate copy constructor, and not doing so would indeed be shooting yourself in the foot.
- Maxatar 11mo ago