4

I've stumbled across this piece of code to reestablish class invariants:

class Foo {
  // some stuff in here
public:
  void clear() {
    *this = Foo();
    //operator=(Foo()); // commented out in favor of the line above
  }
};
  • I would assume that the call to operator= is legal and works as expected, but will create an unnecessary temporary, in case the class is not movable. So it would probably be more efficient to manually assign default values, which is cumbersome and error-prone if we want to extend the class.
  • *this = Foo(), if allowed, is probably more efficient, as copy elision could work here I assume (regardless of the class being movable).

So my questions are:

  • Is the statement *this = Foo(); legal? If yes, please provide a reference to the standard
  • What is more efficient (providing that the first bullet point is true)?
  • In case Foo is movable.
  • In case it's not.
andreee
  • 4,459
  • 22
  • 42
  • 1
    Why do you think the statement shouldn't be legal? It' s a simple assignment. – πάντα ῥεῖ Aug 19 '19 at 09:32
  • @πάνταῥεῖ because we're overwriting the content referred to by `*this`. I'm not sure if RAII works properly here (i.e. if the current object gets properly destroyed before the new one is assigned etc.) or if there are other side effects. That's why I'm asking :-) – andreee Aug 19 '19 at 09:34
  • Of course this is guaranteed for a simple assignment `Foo f, g; f = g;`, but since `*this` is not a "normal" variable I'm not quite sure if that's allowed. – andreee Aug 19 '19 at 09:36
  • 1
    _"`*this` is not a "normal" variable"_ Dereferencing `this` works like with any other pointer. – πάντα ῥεῖ Aug 19 '19 at 09:39
  • 1
    it is simply an assignment from object of type foo with a new object of foo which results in calling the copy assignment operator. So what is special here? The question is more, if it is a good idea to write such code if user here on so needs 6 comments to get the point :-) – Klaus Aug 19 '19 at 09:40
  • 1
    Assignment does not destroy the object being assigned to. (Unless your custom implementation does it explicitly, in which case it should be rewritten to not do that.) – molbdnilo Aug 19 '19 at 09:42
  • @Klaus: I paraphrased the question title to make my intent clearer - I actually already assumed it's legal, but still I wonder what's the right way to go for clearing the object. – andreee Aug 19 '19 at 09:44
  • @andreee: It is absolutely legal to create a "clean" temporary and overwrite the "own" instance with it in the (automatic generated) copy assignment operator. And the temporary has a good to chance to optimized away. But you also can use a Init() function which can be called from ctor and you clear() function. But this has also drawbacks like using references can not be changed in Init and so on. – Klaus Aug 19 '19 at 09:48
  • @πάνταῥεῖ thanks, FWIW, that's also a satisfactory answer :-) Sometimes the absence of facts in the standard is also good to know... – andreee Aug 19 '19 at 09:50
  • @andreee I can tun my comment into an answer, if you think it's worth it. – πάντα ῥεῖ Aug 19 '19 at 09:51
  • @πάνταῥεῖ I think I'm still not yet sure about what solution will work better (or if there's an even better/cleaner solution for clearing an object's state). – andreee Aug 19 '19 at 09:52
  • I’m tempted to write an answer mentioning in-place destruction followed by placement-new but since that’s generally a bad idea I’ll just note it in this comment for completeness. Do use the `*this` assignment. – Konrad Rudolph Aug 19 '19 at 10:29
  • Presumably the `operator=` properly handles resource management (if any), then that assignment to a default constructed unnamed object will do the right thing. – Eljay Aug 19 '19 at 11:23

1 Answers1

7
  • Is the statement *this = Foo(); legal? If yes, please provide a reference to the standard

That's legal yes. It follows the standard that the value can be assigned through a dereferenced pointer.

I don't think we can find anything in the c++-standard mentioning the situation, since it's not a special situation as you think it is.
Assigning a dereferenced *this pointer works as with any other pointer.

  • What is more efficient (providing that the first bullet point is true)?
    • In case Foo is movable.
    • In case it's not.

There are no differences regarding efficiency. Copy elision will be taken by any decent modern c++ compiler.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • 2
    I believe that the chances of copy elision are slim to none. If the constructor throws an exception, `*this` should not change state. Therefore the new instance of `Foo` must be constructed first, then assigned. And if there is no move-assignment operator, how do you elide the copy? Maybe if the construct is `noexcept`, `*this` can be destroyed first before being constructed in place. – Sam Varshavchik Aug 19 '19 at 10:46
  • @SamVarshavchik It depends on what that constructor is doing. If constant propagation is enough to prove that the constructor or destructor don't have side effects (no dynamic memory allocations for instance) the copy can be elided, otherwise it can't. – Cubic Aug 19 '19 at 13:16