Like the type-specific assignment operators, the copy constructor and assignment operators have to test the discriminant to know how to copy the given value. To do this common work, we’ll define a member named copyUnion
.
When we call copyUnion
from the copy constructor, the union
member will have been default-initialized, meaning that the first member of the union
will have been initialized. Because our string
is not the first member, we know that the union
member doesn’t hold a string
. In the assignment operator, it is possible that the union
already holds a string
. We’ll handle that case directly in the assignment operator. That way copyUnion
can assume that if its parameter holds a string
, copyUnion
must construct its own string
:
void Token::copyUnion(const Token &t)
{
switch (t.tok) {
case Token::INT: ival = t.ival; break;
case Token::CHAR: cval = t.cval; break;
case Token::DBL: dval = t.dval; break;
// to copy a string, construct it using placement new; see (§ 19.1.2 (p. 824))
case Token::STR: new(&sval) string(t.sval); break;
}
}
This function uses a switch
statement (§ 5.3.2, p. 178) to test the discriminant. For the built-in types, we assign the value to the corresponding member; if the member we are copying is a string
, we construct it.
The assignment operator must handle three possibilities for its string
member: Both the left-hand and right-hand operands might be a string
; neither operand might be a string
; or one but not both operands might be a string
:
Token &Token::operator=(const Token &t)
{
// if this object holds a string and t doesn't, we have to free the old string
if (tok == STR && t.tok != STR) sval.~string();
if (tok == STR && t.tok == STR)
sval = t.sval; // no need to construct a new string
else
copyUnion(t); // will construct a string if t.tok is STR
tok = t.tok;
return *this;
}
If the union
in the left-hand operand holds a string
, but the union
in the right-hand does not, then we have to first free the old string
before assigning a new value to the union member. If both union
s hold a string
, we can use the normal string
assignment operator to do the copy. Otherwise, we call copyUnion
to do the assignment. Inside copyUnion
, if the right-hand operand is a string
, we’ll construct a new string
in the union
member of the left-hand operand. If neither operand is a string
, then ordinary assignment will suffice.
Exercise 19.21: Write your own version of the Token
class.
Exercise 19.22: Add a member of type Sales_data
to your Token
class.
Exercise 19.23: Add a move constructor and move assignment to Token
.
Exercise 19.24: Explain what happens if we assign a Token
object to itself.
Exercise 19.25: Write assignment operators that take values of each type in the union
.