Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ primer 5th edition. Union and members of class type

Tags:

c++

unions

I have this text from C++ primer 5th edition. ch 19.6 Union:

class Token {
public:
    Token(): tok(INT), ival{0} { }
    Token(const Token &t): tok(t.tok) { copyUnion(t); }
    Token &operator=(const Token&);
    ~Token() { if (tok == STR) sval.~string(); }
    Token &operator=(const std::string&);
    Token &operator=(char);
    Token &operator=(int);
    Token &operator=(double);
private:
    enum {INT, CHAR, DBL, STR} tok; // discriminant
    union {                         // anonymous union
        char   cval;
        int    ival;
        double dval;
        std::string sval;
    }; // each Token object has an unnamed member of this unnamed union type
    // check the discriminant and copy the union member as appropriate
    void copyUnion(const Token&);
};

Token &Token::operator=(const std::string &s)
{
    if (tok == STR) // if we already hold a string, just do an assignment
       sval = s;
    else
       new(&sval) string(s); // otherwise construct a string
    tok = STR;                // update the discriminant
    return *this;
}

In this case, if the union already holds a string, we can use the normal string assignment operator to give a new value to that string. Otherwise, there is no existing string object on which to invoke the string assignment operator. Instead, we must construct a string in the memory that holds the union. We do so using placement new (§ 19.1.2, p. 824) to construct a string at the location in which sval resides. We initialize that string as a copy of our string parameter. We next update the discriminant and return.

Everything is so straightforward for me but what confuses me is this version of the copy-assignment operator (that takes an std::string): and the last paragraph: "if the union already holds a string..." But in case the union doesn't hold a string then why we bother to use such placement new? as long as the assignment operator calls the destructor thus destroys the object then frees memory which is the reverse of the constructor?

I mean why he didn't directly write this?

sval = s;

I think there's no need to call the dtor explicitly with sval.~string() because the assignment operator does that instead. I mean no condition is required.

like image 648
Itachi Uchiwa Avatar asked Jan 25 '23 06:01

Itachi Uchiwa


1 Answers

You cannot assign something to an object that was never constructed. In C++, any object, any object all, needs to be constructed first, before anything happens to it. This is one of the fundamental rules of C++, without any exceptions or alternatives.

Attempting to use an object in any way, before it is constructed, results in undefined behavior. Assigning to an object counts as using it. Because, after all, if you're assigning something to an object it must exist already.

This is a key, fundamental concept. There is a big difference between constructing an object and assigning something to it. In the first case the object did not exist originally. In the second case it already exists, which means that at some unspecified prior point in time it must've been constructed.

You are proposing, effectively, to assign something to an object without the benefit of constructing it. This is undefined behavior.

P.S. In modern C++, you can pretty much ignore everything you've read, and simply use std::variant, which takes care of all of these details for you. It is true, however, that it is important to understand these kinds of fundamental concepts, and this is a good example that illustrates them. But after you've figured out why things have to be the way they are, here, you can pretty much forget about it, and just use std::variant.

I think there's no need to call the dtor explicitly with sval.~string() because the assignment operator does that instead.

No, as a general rule: the assignment operator does not invoke a destructor. The assignment operator, in general, does not destroy an object. If it were, the object would no longer exist, but it does after it's assigned to, of course.

like image 51
Sam Varshavchik Avatar answered Jan 30 '23 12:01

Sam Varshavchik