8

Possible Duplicate:
How can moved objects be used?
What constitutes a valid state for a “moved from” object in C++11?

When implementing move semantics in C++11, should the moved-from object be left in a safe state, or can it be just left in a "junk" state?

e.g. What is the preferred option to implement move constructor in the following example of a C++11 wrapper to a raw FILE* resource?

// C++11 wrapper to raw FILE*
class File
{
  FILE* m_fp;

public:

  // Option #1
  File(File&& other)
    : m_fp(other.m_fp)
  {
    // "other" left in a "junk" state
  }

  // Option #2
  File(File&& other)
    : m_fp(other.m_fp)
  {
    // Avoid dangling reference in "other"
    other.m_fp = nullptr;
  }

  ...
};
Community
  • 1
  • 1
Mr.C64
  • 37,988
  • 11
  • 76
  • 141
  • Will the 'junk' object's destructor not be called? If it will and destructor calls `fclose (m_pf)` then it will surely screw everything up –  Oct 30 '12 at 11:18

2 Answers2

6

The only thing you must be able to do with a moved-from object is destroy it. Beyond that, it's up to your class what the normal class invariants are and whether moved-from objects bother to satisfy them.

For example, it's a good idea to ensure that you can assign to the object, just in case someone wants to use std::move on an instance and give it a new value later. [Edit: as pointed out in an answer to one of the suggested-dupe questions, the general std::swap template moves from an object and then move-assigns to it, so if you don't ensure this then either you need to specialize std::swap or you need to forbid users of your class from using it.]

Since your class does nothing in its destructor, either option is fine. Option 2 might be easier for users to work with, but then again if they're coding on the assumption that they can't do anything with a moved-from object, then it makes no difference. Since the class is incomplete, though, that might change when you write the destructor.

Steve Jessop
  • 257,525
  • 32
  • 431
  • 672
  • Thanks: the "key" piece of information for me is that _the destructor can be called on a moved-from object_. So if the `File` wrapper manages the `FILE*`, then `other.m_fp` must be cleared with `nullptr`, to avoid multiple-closings of the same `FILE*`. – Mr.C64 Oct 30 '12 at 11:26
  • +1. But I recall getting some stick for saying that in a similar post a while back. I think the argument was that the standard doesn't always require things to be destructible. I guess it stems from the difference between what is strictly required by the standard and what is sensible in most day to day situations. – juanchopanza Oct 30 '12 at 11:27
  • @juanchopanza: yes, I suppose if you hedge enough about the valid uses of the type, you could arrange that a moved-from object must not be destroyed without the user first doing something to make it safe. You'd in effect have to forbid construction from temporaries and suchlike, because the user wouldn't have the opportunity to make it safe before it's actually destroyed. As you say, it's conforming code but either misses the point or is *very* special-purpose. – Steve Jessop Oct 30 '12 at 11:30
  • 1
    :( Move semantics breaks RAII in my mind. – Lightness Races in Orbit Oct 30 '12 at 11:30
  • @juanchopanza: come to think of it, if you forbid users from instantiating the class, then the move constructor can provoke UB immediately for all the standard cares :-) "Must" here is a practical necessity rather than a requirement of the standard. – Steve Jessop Oct 30 '12 at 11:32
  • @LightnessRacesinOrbit: well, the model of RAII that you have in your mind is under your control, and C++11 isn't. So I guess you'll have to change the former... – Steve Jessop Oct 30 '12 at 11:35
  • For the record, this is [the related question](http://stackoverflow.com/questions/12095048/what-constitutes-a-valid-state-for-a-moved-from-object-in-c11/12095175#12095175). – juanchopanza Oct 30 '12 at 11:39
  • @SteveJessop: I'd rather continue using the model of RAII that follows from the _definition_ of RAII, if you don't mind. Objects in indeterminate states in C++ offend me :( At least they _can_ be made determinate (e.g. `std::unique_ptr` becomes a null pointer) – Lightness Races in Orbit Oct 30 '12 at 11:43
  • @LightnessRacesinOrbit: obviously you're free to do as you please, but if you use an idiom in C++11 that's broken in C++11, then your code's going to be broken, isn't it? It would seem to me more sensible to revise your preferred definition of RAII. – Steve Jessop Oct 30 '12 at 11:44
  • @SteveJessop: Yes, of course... did I ever claim otherwise? I merely made the point that the idiom _is_ broken in C++11, and I think that this really stinks. It's not "my preferred definition of RAII". – Lightness Races in Orbit Oct 30 '12 at 11:45
  • @LightnessRacesinOrbit: I don't think the idiom is broken in C++11. I think your version of the idiom must include some unnecessary restriction, which is broken by C++11. Hence my advice that the pragmatic option for you is to use a more C++11-friendly definition of the idiom. I suppose you could instead stick to your definition of RAII, but define a new thing called RAII-11 which you use in C++11 code, and which is identical to what everyone else is now calling RAII ;-p – Steve Jessop Oct 30 '12 at 11:46
  • @SteveJessop: Would you disagree that the RAII model, at its most basic, involves objects never being in some unusable state? Because they are constructed at allocation, and destroyed at de-allocation? Move semantics patently breaks that model. Maybe I'm missing something, but I can't see that a hypothetical "RAII-11" would be anything but an empty definition (well, for the destruction case at least). – Lightness Races in Orbit Oct 30 '12 at 11:50
  • @LightnessRacesinOrbit Move semantics are available in C++03 too, it is just that you have to manually support them with funny move and ownership objects, and then it leaks if you don't attach the return value of your "move" to the object. The 3rd answer to this question would be to have some kind of ownership flag that you change the state of in the object being moved, but really the "moved" object should be treated like it isn't there so just null the pointer. – CashCow Oct 30 '12 at 11:57
  • @LightnessRacesinOrbit: Look at it this way -- you're not obliged to implement destructive moves for your types, or use `std::move` on types that have them. C++11 imposes on you that a temporary object might become "unusable" shortly before it is destroyed, during an interval in which in any case you've lost your reference to it. `std::move` allows you voluntarily to put an object into a mostly-unusable state as an optimization, but anyway "usable" is a gross over-simplification of what RAII really guarantees. For a "usable" C++03 file object all you can certainly do is check its error state. – Steve Jessop Oct 30 '12 at 11:57
  • @SteveJessop: Okay, that helps – Lightness Races in Orbit Oct 30 '12 at 11:59
  • @LightnessRacesinOrbit: For another consideration, does the basic exception guarantee violate RAII? I'm willing to believe that it does in a rigorous definition of RAII (and hence that code claiming to "use RAII" should enforce at least the strong exception guarantee for all operations), but I don't think that's necessarily the common view. – Steve Jessop Oct 30 '12 at 12:02
  • @SteveJessop: No, because invariants must be preserved. Then again, whether move semantics breaks that would depend on whether you just take away most of your invariants. – Lightness Races in Orbit Oct 30 '12 at 12:13
  • @LightnessRacesinOrbit: exactly, the basic exception guarantee doesn't *inherently* provide any more or fewer invariants for the class than moving from an instance. It's up to you what's a "valid object" after an exception is thrown, and what's a "valid object" after a move. As it happens, though, standard classes do provide quite good exception guarantees but almost nothing after a move, and I agree that there's a difference in practice between what people tend to consider a "valid" state after the two different things. – Steve Jessop Oct 30 '12 at 12:20
  • For this `File` example, if it has a constructor that sets `m_fp` to null then I think option 2 leaves it in a "valid state" for all RAII purposes. If it doesn't have any such constructor, then it's more dubious whether set-to-null ought to be allowed by the class invariants, and whether it's sound RAII practice to allow the user to invoke an otherwise impossible state by moving from an instance (maybe not, but it's always the user's choice). – Steve Jessop Oct 30 '12 at 12:22
6

An object that has been moved from is still an object, and it must be in a valid state, although it may be indeterminate. In particular, it should be possible to assign a new value to it safely (and of course it must be destructible, as @Steve says). It's up to you what particular semantics you want to give your class, as long as a moved-from object remains valid.

In general, you should think of "move" as an optimized "copy". However, for some classes that are in­trin­sically "move-only", such as unique_ptr, additional guarantees may be appropriate – for example, unique_ptr promises that after it has been moved-from it is null, and surely nothing else really makes sense.

(Your actual code is incomplete, but given that a FILE* is a sort-of move-only resource, it is probably broken and you should try to emulate unique_ptr as closely as possible – or even use it directly.)

Community
  • 1
  • 1
Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025