2

Sometimes I want to make classes/structs with const members. I realize that this is a bad idea for multiple reasons, but for the sake of argument let's pretend the only reason is that it makes a well-formed operator = a hassle, to say the least. However, I contrived a fairly simple work-around to it, as demonstrated by this struct:

struct S {

  const int i;

  S(int i) : i(i) {}

  S(const S& other) : i(other.i) {}

  S& operator =(const S& other) {
    new (this) S(other);
    return *this;
  }

};

Ignoring destructors and move semantics, is there any really big reason why this shouldn't be done? It seems to me like a more type-safe version of

S& operator =(const S& other) {
  const_cast<int&>(i) = other.i;
  return *this;
}

So, the summary of the question is this: is there any major reason placement-new should not be used to implement copy assignment to have the same semantics as a copy construction?

Anonymous
  • 491
  • 2
  • 12
  • 6
    I wonder why you want to have a class with `const` members _and_ an `operator=`. That smells like a design issue to me. – Thomas Jul 12 '19 at 15:54
  • 1
    Like I said, ignore the other issues with that pattern. This is hypothetical; I won't be actually using it, regardless of answers to the question. – Anonymous Jul 12 '19 at 15:56
  • 1
    See [this](https://stackoverflow.com/a/47475556/4342498) and [this](https://stackoverflow.com/questions/47473621/placement-new-and-assignment-of-class-with-const-member/47475941#47475941) – NathanOliver Jul 12 '19 at 15:59
  • I suggest this: https://isocpp.org/wiki/faq/assignment-operators – SergeyA Jul 12 '19 at 16:01
  • Doesn't this leak? When is the `delete` happening if you're calling `new` on assignment? – MartinVeronneau Jul 12 '19 at 16:05
  • 4
    @MartinVéronneau Placement new doesn't allocate any memory, it just uses the memory that's already there. – Anonymous Jul 12 '19 at 16:06
  • Oh, okay! Heh. I think I'll need to look into this! Nothing to feel like an old-timer than being reminded that you're a bit out of touch with the new features of the very language you're using each day. – MartinVeronneau Jul 12 '19 at 16:10
  • Constructors create new objects. Assignment operators modify existing objects. Sooner or later, an assignment operator that uses a constructor to create a new object in place will bite you. – Pete Becker Jul 12 '19 at 16:37

2 Answers2

4

I don't believe that placement new is a problem here but the const_cast which produces undefined behavior:

C++ 10.1.7.1-4 Except that any class member declared mutable (10.1.1) can be modified, any attempt to modify a const object during its lifetime (6.6.3) results in undefined behavior.

You'll probably get away with this until compiler starts to optimize things.

The other problem is the use of a placement new on a piece memory occupied by living (non-destroyed) object. But you'll probably get away with this while object in question has a trivial destructor.

Adam Sosnowski
  • 1,126
  • 9
  • 7
  • "*But you'll probably get away with this while object in question has a trivial destructor.*" Not with that `const` member in there. – Nicol Bolas Jul 12 '19 at 16:45
  • 1
    It's worth adding that if you have a non-trivial destructor, you need to explicitly call the destructor: `this->~S();` before using placement new: `new (this) S(...);` – alter_igel Jul 12 '19 at 17:11
3

is there any really big reason why this shouldn't be done?

  1. You must be absolutely sure that every derived class defines its own assignment operator, even if it is trivial. Because an implicitly defined copy-assignment operator of a derived class will screw everything. It'll call S::operator= which will re-create a wrong type of object in its place.

  2. Such destroy-and-construct assignment operator can't be re-used by any derived class. So, not only you are forcing derived classes to provide an explicit copy operator, but you're forcing them to stick to the same destroy-and-construct idiom in their assignment operator.

  3. You must be absolutely sure that no other thread is accessing the object while it is being destroyed-and-constructed by such assignment operator.

  4. A class may have some data members that must not be affected by the assignment operator. For example, a thread-safe class may have some kind of mutex or critical section member, with some other thread waiting on them right when the current thread is going to destroy-and-construct that mutex...

  5. Performance-wise, it has virtually no advantage over standard copy-and-swap idiom. So what would be the gain in going through all the pain mentioned above?

Igor G
  • 1,838
  • 6
  • 18