10

If I move-construct a from b, is it still necessary to destruct b, or can I get away without doing so?

This question crossed my mind during the implementation of an optional<T> template. Excerpt:

~optional()
{
    if (initialized)
    {
        reinterpret_cast<T*>(data)->~T();
    }
}

optional(optional&& o) : initialized(o.initialized)
{
    if (initialized)
    {
        new(data) T(std::move(*o));   // move from o.data
        o.initialized = false;        // o.data won't be destructed anymore!
    }
}

Of course, I could just replace the bool initialized with a three-valued enumeration that distinguishes between initialized, non-initialized and moved-from. I just want to know if this is strictly necessary.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • Why using destructor? why not delete? after you move, you ca set it to 0. – balki Aug 04 '11 at 15:22
  • 2
    @balki: Not sure what you're getting it. There is no dynamic memory in my `optional` template, so there's nothing to `delete`. – fredoverflow Aug 04 '11 at 15:29
  • 1
    I was implementing the exact same thing, and had the exact same question! Thanks! – VF1 Jan 19 '14 at 02:29

3 Answers3

14

Yes, it is still necessary to destruct b. A moved from object is a valid, constructed object. In some cases, it may even hold resources that still need to be disposed of. In generic code such as you show, T may not even have a move constructor. You may invoke a copy constructor instead in this case. So you can definitely not assume that ~T() is a no-op and can be elided.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
3

Yes, you do still have to destruct them. One of the designs that can show this flaw is for example, observer-based patterns where one object keeps lists of pointers to another. Not running the destructor won't remove the pointer and the code will crash when it attempts to access an object that no longer exists.

The easier thing to do in your example is just to not set initialized to false in the moved-from object. The value is still defined to be in a valid state after being moved from, and the destructor of the rvalue you're referring to would clean it up with no further intervention.

Puppy
  • 144,682
  • 38
  • 256
  • 465
2

I'd like to answer 'No' to your question but I'm not sure it's even the right question to ask. Consider the following:

{  // start of scope
    T maybe_moved;
    if(some_condition) {
        T(std::move(maybe_moved));
    }
// end of scope
}

T::~T() should obviously be called only once for the maybe_moved object. If a move constructor would call it, how would you make such innocuous code work?

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • My move constructor does not call any destructors...? – fredoverflow Aug 04 '11 at 15:29
  • @FredOverflow Presumably whatever hypothetical mechanism destroy moved-from objects in your question applies. – Luc Danton Aug 04 '11 at 15:40
  • @Barry OPs question pertains to user-defined special members, not how the language is specified. As I’ve put in my answer, how can the *move constructor* perform this duty (i.e. from within the if statement, in this case). – Luc Danton Mar 07 '16 at 05:54