Assignment operator overloading
Community Forums/Monkey2 Talk/Assignment operator overloading
| ||
'Operator=' is used as comparison for equal. What is the operator for operator-overloading assignments? (theObj = otherObj) Also used as copy-operator? Edit: Help says '=' assignment operator can't be overloaded... hmm. Would be nice to get assignment/copy too. '+=' etc. already working, just plain '=' is missing. |
| ||
Normal assignment is a bit special, because it's sort of tied up with how the compiler actually works. The compiler really needs assignment to work 'as expected' for several operations such as initializing variables, copying temporaries around etc. This differs from "operator+" etc in that the compiler doesn't depend on these in any way. In fact, by the time the code is translated these have been converted to simple method calls. Not so assignment which is a 'real' backend op and can do 'magic' things that a normal method can't. Overloading assignment doesn't even make much sense for reference types, eg: Class C Field x:T Method Assign( c:C ) Self.x=x End End Function Main() Local c:C c=New C 'Null object error! End It makes more sense for structs, but again it's something that's incredibly useful for the compiler to have control over, so it can safely create as many 'temporaries' as it wants, reliably initialize vars and so on. I guess the compiler could in theory have an 'internal' assignment op, but depending on what you're doing this may defeat the purpose of taking over assignment (eg: smart pointers). In my experience, in about 93% of cases assignment overloading (in c++ anyway) is used to perform automatic type conversions, eg: Struct Vec3 End Struct Vec4 Method Assign( v:Vec3 ) 'prob. not a good idea... End End I'm am not totally against custom automatic conversions (although I'm less keen on them than I used to be) but I don't think this is the right way to do it. Instead, I think something like this would be better: 'global automatic conversion function Function To:Vec4( v:Vec3 ) Return New Vec4( v,1 ) End But that's a way off yet. |
| ||
Okay, looks like we need to import C++ operator= as a method:struct sVec4 { // ... void operator=(const sVec4& other) { x = other.x; y = other.y; z = other.z; w = other.w; } }; In MX2: Extern Struct sVec4 Method Assign( other:sVec4 ) = "operator=" End I have several external C++ classes here that overload operator== and operator=, that’s the reason I was asking. template <class T> class vector2d { public: vector2d<T>& operator= (const vector2d<T>& other) { X = other.X; Y = other.Y; return *this; } vector2d<T>& operator= (const dimension2d<T>& other) { X = other.Width; Y = other.Height; return *this; } bool operator==(const vector2d<T>& other) const { return equals(other); } It’s not uncommon to overload the assignment operator in C++. Also copy-operator. For string-pointers etc. it’s actually required to differentiate if the pointer is copied, or if a new memory is allocated and the content is copied (new string, for example). c=New C 'Null object error! Of course ‚New‘ would call Operator New, the constructor. ;) In C++ it’s two different things: c = new C() ' create c = b ' copy / assignment |
| ||
I am glad it is not allowed. Otherwise, at every assignment you can't be sure what's going on behind the scenes. And assigning is one of the most frequent operations you do. Just it being a possibility increases your program complexity and of course, the chance of bugs. I think we are better off not paying this constant thinking tax. |
| ||
The assignment operator for sVec4=sVec4 can probably be skipped since it just does a straight copy in this case so should just work. For assignment of other types, yes you'll currently need to use a method - I'd probably just go for 'Set' myself. For comparisons, this should work: Struct sVec4 Operator=:Bool( t:T )="operator==" Operator<>:Bool( t:T )="operator!=" ...etc... End To add support for the 'sort' operator, this *may* work: Struct SVec4 Operator<=>:Int( rhs:sVec4 ) Extension="bbCompare" End ...as long as sVec4 has defined 'operator<'. > It’s not uncommon to overload the assignment operator in C++. Also copy-operator. And IMO, this is an area where c++ is a real PITA. You can end up with a ton of 'conversion' methods in copy ctors, assignments etc., when really you should only need to implement type1->type2 *once*. And most copy ctors and assignments *are* just doing conversions... Some of this extra crap is worth it in c++ because you get to optimize certain things in a bit more depth (eg: copy ctors can/must ignore the values currently in fields because they're uninitialized; assignment operators can't). But a lot of it just doesn't make sense for mx2. > Of course ‚New‘ would call Operator New, the constructor. ;) The problem is not with the New, but with the assignment. The variable 'c' will be null when its assignment operator is invoked, so you'll be invoking a method on a null object. |
| ||
Class/Struct Location pt:Point ptr End Local a:Location, b:Location a = New Location() b = a ' << does it copy pointers only? ' << or does it copy the contents of pt? Looks like having no copy/assignment operator can be a problem and confusing/unclear (IMHO). More so when interfacing with C++. ;) |
| ||
> Looks like having no copy/assignment operator can be a problem and confusing/unclear (IMHO). Not sure what you mean here - do you want different operators for 'assign struct instance' and 'assign class instance'? |
| ||
@Mark: Take the last code. If "b = a“ copies the pointers like 'b.pt = a.pt' only, they point to the same memory location. Changing 'a.pt.x‘ later, changes also 'b.pt.x‘ - because we just copied the pointer ‚pt‘, not the content. To prevent this, in C++ you write a copy-operator that allocates new memory for b.pt and copies the content of a.pt into it. Using this copy/assignment operator, after using "b = a“ -->> both objects have their own memory location for pt. Changing a.pt.x does not change b.pt.x anymore. .pt does not point to the same memory anymore because the copy/assignment-operator made a real copy, instead of copying pointer values only. That’s the power of C++, and in MX2 I actually don’t know what it does. I would guess „b = a“ copies pointers only for .pt, and a.pt and b.pt point to the same thing afterwards - which is not what I want. A copy/assignment operator can determine such behaviour. |
| ||
Still don't really get it sorry... I am guessing what you're really after is something like Reference<T> (or 'safe pointers'), so that the struct/class distinction can be removed - but that ain't happening. If 'b' is a struct, 'b=a' will copy the struct contents. If 'b' is a class, only a reference will be copied. It may not be clear what 'b=a' is doing (is it in any language?) but it is at least 'well defined'! There will always be stuff you can do in c++ that you can't do in mx2. But I am a reasonably experienced c++ coder who knows a fair bit about copy constructors and assignment overloading and conversions operators etc (and the huge amount of complexity and subtle side effects they introduce) and I am entirely happy with the compromises made for mx2, such as the struct/class distinction and the lack of assignment operator. Either way, I struggle to see how the ability to overload assignment in ANY language can make it *clearer* what an assignment is doing! It *may* be copying a value, it *may* be copying an internal reference, it *may* open a window and print 'APRIL FOOLS'! |
| ||
No problem. Just forget it, and thanks anyway! |
| ||
@marksibly: I wouldn't throw out the idea, especially in C++. However, Monkey doesn't really need R-values as much as it needs C++'s optimization of R-values on the backend. I'd definitely think assignment operator overloading for structures should be a thing, though. As for what Danilo was trying to explain, he's talking about deep copies vs. shallow copies. In the case of using pointers or highly controlled references for heap-based storage, having the assignment/copy constructor perform a deep copy of everything is ideal. However, this doesn't have much to do with move-semantics, and has much more to do with state-coherence. In other words, having the structures keep their behavior regardless of copies. A trait that would need heavy personal control over shallow copies, and when they should happen. In the case of C++ you have move semantics to go a step further, but I think the simplest way to handle this is a copy-constructor. I haven't been keeping up with Monkey too much, but wouldn't a copy-constructor effectively be the same thing as your 'To' function? It makes sense to me to have this constructor for better control of resources. For classes, definitely not, but for structures, this is ideal. Trust me, your reservations toward assignment operators and copy constructors are nothing compared to the bugs associated with only having raw copies of structures everywhere. Just imagine if one decided to destroy a resource while a copy lived somewhere waiting to be used incorrectly. C++ already has move semantics, so the reduction of extra copies is already mostly implicit. Sure, you wouldn't be able to take full advantage of it, but we also have other means to that end anyway. By the way, I'm sure this has been asked already, but any thoughts about BlitzMax's 'Var' coming back? Structures lose a lot of their modern uses without temporary references. As long as you restrict getting a pointer to a "struct-reference", you'd never have the problem of incorrect storage. Even if you didn't restrict raw memory access to struct-references, it's not like it's much of a pitfall. |
| ||
In PHP $a =1; $b=$a; // 1 $c=&$a; // ref , will change along with |
| ||
ImmutableOctet(SKNG) wrote: As for what Danilo was trying to explain, he's talking about deep copies vs. shallow copies. In the case of using pointers or highly controlled references for heap-based storage, having the assignment/copy constructor perform a deep copy of everything is ideal. Thanks, exactly what I was trying to explain with my limited knowledge of the english language. - Object copying - Methods of copying - Object copying - Implementation Nearly all object-oriented programming languages provide some way to copy objects. As the majority of languages do not provide most objects themselves, the programmer has to define how an object should be copied, just as he or she has to define if two objects are identical or even comparable in the first place. Many languages provide some default behavior. |
| ||
I would recommend a 'Method Copy:T()' for deep copies. This is IMO much more flexible than a 'copy constructor' because it can do trickier stuff like can return an existing copy, a subclass of the return type etc. Also, if you've only got a reference to a base class and want to be able to deep copy it, you don't have much choice but to do it this way. I don't understand the logic behind *always* performing deep copies on class instances - this would make it impossible to reference them indirectly because each assignment would create a new copy, eg: pushing an Actor on a stack would push a copy of the actor. This is what structs are really for. Struct instances are always shallow copied because they are 'value types', like ints, floats etc. If you get to the point where it's necessary to deep copy a struct, I'd recommend thinking about making it a class instead, along with a Copy() method. Structs are really meant for simple, primitive types like Vec3, Mat4 etc. Structs that don't contain any references types never need anything more than a shallow copy as there's no indirection involved. Class instances are never deep copied because they are passed by reference - this (and virtual methods) is kind of the point of classes, as without references you don't get indirection (easily). |
| ||
Thanks! |
| ||
I think the real issue is people slowly realising how much was lost when they abandoned the C++ object model. In C++ there's no real distinction between structs and classes and the programmer has complete control over how an object is created, copied or referenced. You're not going to get that flexibility back even when you split objects into two different kinds with different semantics. |