Alex Rivera | Logout

const member and assignment operator. How to avoid the undefined behavior?

Asked 2010-11-09T16:42:45.020
46

I answered the question about std::vector of objects and const-correctness, and received a comment about undefined behavior. I do not agree and therefore I have a question.

Consider the class with const member:

class A { 
public: 
    const int c; // must not be modified! 
    A(int c) : c(c) {} 
    A(const A& copy) : c(copy.c) { }     
    // No assignment operator
}; 

I want to have an assignment operator but I do not want to use const_cast like in the following code from one of the answers:

A& operator=(const A& assign) 
{ 
    *const_cast<int*> (&c)= assign.c;  // very very bad, IMHO, it is undefined behavior
    return *this; 
} 

My solution is

// Custom-defined assignment operator
A& operator=(const A& right)  
{  
    if (this == &right) return *this;  

    // manually call the destructor of the old left-side object
    // (`this`) in the assignment operation to clean it up
    this->~A(); 
    // use "placement new" syntax to copy-construct a new `A` 
    // object from `right` into left (at address `this`)
    new (this) A(right); 
    return *this;  
}  

Do I have undefined behavior (UB)?

What would be a solution without UB?

Edit
Report

2 Answers

1

In absence of other (non-const) members, this doesn't make any sense at all, regardless of undefined behavior or not.

A& operator=(const A& assign) 
{ 
    *const_cast<int*> (&c)= assign.c;  // very very bad, IMHO, it is UB
    return *this; 
}

AFAIK, this is no undefined behavior happening here because c is not a static const instance, or you couldn't invoke the copy-assignment operator. However, const_cast should ring a bell and tell you something is wrong. const_cast was primarily designed to work around non const-correct APIs, and it doesn't seem to be the case here.

Also, in the following snippet:

A& operator=(const A& right)  
{  
    if (this == &right) return *this;  
    this->~A() 
    new (this) A(right); 
    return *this;  
}

You have two major risks, the 1st of which has already been pointed out.

  1. In presence of both an instance of derived class of A and a virtual destructor, this will lead to only partial reconstruction of the original instance.
  2. If the constructor call in new(this) A(right); throws an exception, your object will be destroyed twice. In this particular case, it won't be a problem, but if you happen to have significant cleanup, you're going to regret it.

Edit: if your class has this const member that is not considered "state" in your object (i.e. it is some sort of ID used for tracking instances and is not part of comparisons in operator== and the like), then the following might make sense:

A& operator=(const A& assign) 
{ 
    // Copy all but `const` member `c`.
    // ...

    return *this;
}
answered 2010-11-09T17:04:38.557
0

Have a read of this link:

http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=368

In particular...

This trick allegedly prevents code reduplication. However, it has some serious flaws. In order to work, C’s destructor must assign NULLify every pointer that it has deleted because the subsequent copy constructor call might delete the same pointers again when it reassigns a new value to char arrays.

answered 2010-11-09T17:04:07.373

Your Answer