Assignment takes in two objects, the object to be assigned to, and the object with the desired value.
Assignment should:
- modify an existing object, the object being assigned to
Assignment should not:
- create a new object
- modify the object that has the desired value.
So let's say the object type we're dealing with is:
struct S { int i; float f; };
A function that performs assignment might have the signature:
void assignment(S &left_hand_side, S const &right_hand_side);
And it would be used:
S a = {10, 32.0f}; S b; assignment(b, a);
Notice:
- the left hand side object is modifiable, and assignment will modify it, rather than create a new object.
- the right hand side object, the object with the desired value, is not modifiable.
Additionally, in C++ the built-in assignment operation is an expression rather than a statement; it has a value and can be used as a subexpression:
int j, k = 10; printf("%d", (j = k));
This sets j equal to k, and then takes the result of that assignment and prints it out. The important thing to note is that the result of the assignment expression is the object that was assigned to. No new object is created. In the above code, the value of j is printed (which is 10, because j was just assigned the value 10 from k).
Updating our earlier assignment function to follow this convention results in a signature like:
S &assignment(S &left_hand_side, S const &right_hand_side);
An implementation looks like:
S &assignment(S &left_hand_side, S const &right_hand_side) { // for each member of S, assign the value of that member in // right_hand_side to that member in left_hand_side. left_hand_side.i = right_hand_side.i; left_hand_side.f = right_hand_side.f; // assignment returns the object that has been modified return left_hand_side; }
Note that this assignment function is not recursive; it does not use itself in the assignment, but it does use assignment of the member types.
The final piece of the puzzle is getting the syntax a = b to work, instead of assignment(a, b). In order to do this, all you have to do is make assignment () a member function:
struct S { int i; float f; S &assignment(S &left_hand_side, S const &right_hand_side) { left_hand_side.i = right_hand_side.i; left_hand_side.f = right_hand_side.f; return left_hand_side } };
Replace the left_hand_side argument with *this:
struct S { int i; float f; S &assignment(S const &right_hand_side) { this->i = right_hand_side.i; this->f = right_hand_side.f; return *this; } };
And rename the function to operator=:
struct S { int i; float f; S &operator=(S const &right_hand_side) { this->i = right_hand_side.i; this->f = right_hand_side.f; return *this; } }; int main() { S a, b = {10, 32.f}; S &tmp = (a = b); assert(a.i == 10); assert(a.f == 32.f); assert(&tmp == &a); }
Another important thing to know is that the = sign is used in one place that is not assignment:
S a; S b = a; // this is not assignment.
This is 'copy initialization'. It does not use operator=. It is not assignment. Try not to confuse the two.
Points to remember:
- assignment modifies the object being assigned to
- assignment does not modify the object being assigned from.
- assignment does not create a new object.
- assignment is not recursive, except insofar as assignment of a compound object makes use of assignment on all the little member objects it contains.
- assignment returns the object after modifying its value
- copy initialization looks sort of like assignment, but has little to do with assignment.
=shouldn't probably modify its right hand operand.