5 ms·
> defaulted to deep copy semantics It defaulted to pass-by-value, with shallow copy semantics, as opposed to pass by reference.
by _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>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. You either are misspeaking, deeply confused, or quite possibly both.
- HarHarVeryFunny 11mo agoDo you agree that C++ pass-by-value is using copy constructors? Do you agree that the copy constrictors of the C++ standard library (STL) all do deep copy? Do you agree that if you write your own class with a raw pointer member, you would need to write a copy constructor? So, what exactly are you disagreeing about ?! You seem to be saying that if you wrote a C-style structure (no constructors) with a pointer member, then C++ would behave like C, which is true, but irrelevant. We're talking about C++ vs C, but since C++ is a superset of C, then yes you can still screw yourself C-style if you choose to.
- Maxatar 11mo ago>Do you agree that C++ pass-by-value is using copy constructors? C++ is an incredibly complex language that it's almost never the case that you can make a categorical statement this simple and have it be true or false. In C++, pass by value may or may not make use of a copy constructor depending on a set of very complex rules. For example passing an rvalue by value for a class type which defines a move constructor may use the move constructor rather than the copy constructor (emphasis on may because even that isn't guaranteed). Passing an lvalue for a class type may or may not invoke the copy constructor depending on specific language rules regarding copy elision. For fundamental types, there is no copy constructor whatsoever. >Do you agree that the copy constrictors of the C++ standard library (STL) all do deep copy? I absolutely do not agree with this, in fact believing so is absurd and I can't possibly fathom what would lead you to believe this. The principle the C++ standard uses for copying objects is based on Alexander Stepanov's "Elements of Programming" where he defines the concept of a regular type. A regular type does not require making a deep or a shallow copy, what it requires is that copies satisfy the following: The copy must preserve equivalence (if a == b, then copy(a) == copy(b)). The copy must be idempotent in meaning. Copying must not have side effects on the source. So as an example a std::string_view can be copied, but making a copy of it is constant O(1) in both time and space. What is required is that after performing a copy, the original source remains unchanged, operator == returns true between the source and the copy, and that making a copy is an idempotent operation. You can extend this further for other collections as well, a std::vector<Foo*> does not end up making deep copies of the pointers being contained, what it does do is satisfy the conditions for being a regular type. >Do you agree that if you write your own class with a raw pointer member, you would need to write a copy constructor? Absolutely not. Just sticking to the existing standard library types you have things like std::span, all of the iterator types which use pointers under the hood, std::initializer_list, std::string_view was already covered. To be blunt, you're expressing a very superficial understanding of a very complex topic that's part of an even more complex language. In general my advice is... never assume that things in C++ are as simple and straight forward as they appear. If you're using a subset of the language that works for you, great, like really good job and continue doing so and I absolutely don't want to discourage you from using the subet of C++ that works for your use case... but don't then confuse this subset for being representative of either the language as a whole or representative of the vast use cases that C++ belongs to and recognize that the advice you give which works so well for you may actually be really bad advice for someone else working within a different subset of the language.
- jstimpfle 11mo ago"Deep copy" is quite hand-wavy. A useful program is not a collection of trees that you can just copy like that. Instead, typical program's data structures tend to have lots of references that let you go from anywhere to just about anywhere else. That's why "deep copy" is just a rough idea that may work for simple container types but not much else.
- HarHarVeryFunny 11mo agoI didn't choose the language. "Deep copy" is the phrase that everyone uses. At the end of the day C++ copy (incl. pass-by-value) does whatever the copy constructors of the types you are using does, which is going to be a combination of the "copy" semantics of the standard library's types, plus that of your own types. Obviously you could choose to write a copy constructor that violates what most reasonable people might expect of "copy" semantics, but this is going to be rare, and I think conceptualizing C++ copy as "deep/recursive copy" holds up well even if pointers (smart or raw) are involved.
- jstimpfle 11mo agoChasing an object model may work for simple programs, but as the program gets more complex, it tends toward an exercise in futility. The problem with the object concept is that there are many ways to partition all the data in a program into distinct objects. But types and object models need you to decide on one way of looking at the world, and it wants you to see the world like that throughout the codebase. Taking copying as a specific example, there may well be more than one useful way of copying stuff. The best way to achieve what you need depends not only on functional factors but also non-functional factors (such as performance and concurrency for example). The more serious and involved a program gets, the more apparent it becomes that there is not one way to copy. And the harder and more arbitrary it becomes to single out one way of copying that should be "blessed" with a special syntax. It's an arbitrary choice to single out a blessed way to copy, and that is also a choice against a lot of other useful ways to copy stuff. Having one blessed way makes a few things easier, but makes everything more complicated. There is now one way to copy that gets done quite implicitly using syntax, which draws on a mountain of special C++ semantics, and there are other ways that must be done using regular function calls. The end result is something that is more complex than just always making regular freestanding functions that do the thing that you want done. (What copy routine will be chosen implicitly by syntax/semantics is mostly predicated on types, so you can always opt to add wrapper types to accomodate other ways of doing things. Now try wrapping the members inside the thing that you want copied, working against all the implicit semantics... that must be about the point where the boilerplate becomes unbearable, where C++ gets much more verbose and unreadable than just straightforward C). The downside of always using function calls is that you can't profit from all the built-in implicit semantics stuff, but as said there is always a theshold of program complexity beyond which that stops being useful because there's much more stuff that the program needs to do that cannot profit from it.
- _zagj 11mo agoI consider it a "shallow" copy because it doesn't copy pointer or reference members.
- HarHarVeryFunny 11mo agoHow is the compiler meant to guess the semantics of your pointer or reference members? If they are referring to static or external variables, then presumably the default member-wise "shallow" copy is what you want. If your pointer is instead referring to some allocated storage, then presumably you do NOT want to just copy the pointer, but the language is not a mind reader - it doesn't know what your pointer means, and it supports copy constructors precisely so that you can implement whatever is needed. The language is giving you the tools to define how you want your pointer to be "copied"... it's your choice whether an exact "shallow" copy is appropriate, or whether you need to do something else. This is one of many reasons why if you are trying to write modern C++ you should use C++ alternatives to C ones wherever they exist - use STL containers rather than writing your own, use smart pointers rather than raw ones, etc.
- jayd16 11mo agohttps://en.cppreference.com/w/cpp/language/copy_constructor.html https://en.cppreference.com/w/cpp/language/copy_constructor.... > For non-union class types, the constructor performs full member-wise copy of the object's direct base subobjects and member subobjects, in their initialization order, using direct initialization. For each non-static data member of a reference type, the copy constructor binds the reference to the same object or function to which the source reference is bound. Would you call this deep or shallow? I think most would call it a shallow copy.
- HarHarVeryFunny 11mo agoThe default copy constructor does a member-wise copy, but that member-wise copy in turn uses each member's copy constructor (explicit or default), if it has one. So for example, if your class has members whose type is any of the STL container types (std::list, vector, map, set,etc) then you will get a deep copy since that is how those types behave. The semantics of a reference as part of a class are whatever you choose to make them. Is the reference referring to something within the class, maybe initialized in the constructor, or is it referring to something external such as a global variable perhaps? It's up to you to define your constructors according to the semantics of how you are using the reference. If you don't provide a copy constructor then the default member-wise copy would indeed just make a copy of the reference, which would be the right thing if you were using the reference to point to something global, but would be a bug in your code (missing copy constructor) if you needed it to refer to something in the copied class. Raw pointers in classes are much the same. How are you using the pointer, and what does that mean that your copy constructor needs to do? Just like with the reference example, the default member-wise copy will only be correct if you are using the pointer to point to something not owned by the containing class, and just want the copy to point to the same thing. If the pointer is an owning reference to something allocated on the heap, then maybe you would want the copy to allocate it's own storage and copy the contents, but only you would know that. The semantics of smart pointers are more clear cut, which is why they are to be preferred in modern C++ code. A std::unique_ptr simply can't be copied, so you would be forced to write a copy constructor to handle it. The default member-wise copy of a std::shared_ptr will just invoke it's copy constructor resulting in the copy pointing to the same object, now with an increased reference count. Long story short, if you are writing modern C++, using STL container types, then a copy is a deep copy. If you are writing your own container types using pointers, or implementing your own copy constructors, then "copy" means whatever you have chosen it to mean.