8

After moving an object, it must be destructable:

T obj;
func(std::move(obj));
// don't use obj and let it be destroyed as normal

But what else can be done with obj? Could you move another object into it?

T obj;
func(std::move(obj));
obj = std::move(other);

Does this depend on the exact type? (E.g. std::vector could make specific guarantees you can't rely on for all T.) Is it required or even sane that all types support something besides destruction on moved-from objects?

5 Answers5

6

Yes, you can move another object into it. std::swap does this.

Henrik
  • 22,652
  • 6
  • 38
  • 90
  • 1
    I'd forgotten std::swap, good point. –  Oct 06 '10 at 13:05
  • The appears to be the only sane requirement, in addition to destruction, for any type. I'll note that the move-assignment operator could be deleted (thus yielding a compile-time error) and that swap requires MoveAssignable (§20.3.2p1, 0x FCD) to make this work—implying some types, which can't be used with swap, *aren't* MoveAssignable. –  Oct 06 '10 at 13:57
  • And it appears has\_move\_assign (§20.7.4.3, table 45, 0x FCD) tells you whether MoveAssignable is met. –  Oct 06 '10 at 13:58
  • @Roger Concepts were removed from the standard. – Šimon Tóth Oct 06 '10 at 14:12
  • @Let: The referenced sections are not part of the concepts proposal, and the FCD was published ~9 months after concepts were removed. As you put it in another comment, "you should probably read something about [these], you seem to have no idea what they are." –  Oct 06 '10 at 14:18
  • @Roger I didn't know that they tweaked type traits this much. Sorry, should have checked the draft immediately. – Šimon Tóth Oct 06 '10 at 15:04
6

The current draft of C++0x requires that a moved-from object can be destroyed or assigned to. If you pass your object to a function in the standard library then that is all that is assumed.

It is generally considered good practice to ensure that a moved-from object is a "working" object of its type that satisfies all invariants. However, it is in an unspecified state --- if it is a container, you don't know how many elements it has, or what they are, but you should be able to call size() and empty(), and query it.

The current draft is unclear on what is required of the standard library types themselves, and there is active discussion in the C++ committee about that.

Anthony Williams
  • 62,015
  • 12
  • 122
  • 149
  • It may be a good idea to allow users to call a size() method, but if I write my own container, am I allowed to make obj.size() be UB if obj has been moved? –  Oct 06 '10 at 13:16
  • Yes, with your own type you can do what you like. I would strongly recommend adding a `valid()` query if you do though, as undetectable states that "explode" when used are a real pain to deal with. – Anthony Williams Oct 06 '10 at 13:48
  • 1
    I wouldn't say every move-enabled class has to support assignment. But if the class does support some sort of assignment(s) this/these should still be usable in a "moved-from" state. – sellibitze Oct 06 '10 at 20:04
  • As for containers, David Abrahams at least recommends that the move-assigned-to container should destruct its original elements somehow. Consider vector where move assignment is implemented via swap. That's not a good idea because old stream objects might still live on in the other vector instead of being closed. – sellibitze Oct 06 '10 at 20:07
  • The C++0x library requires that a move-enabled class supports assignment to moved-from objects, so if you have a `std::vector` then `MyClass` had better support assignment after move. If you don't use your class with the standard library then you can do what you like. – Anthony Williams Oct 07 '10 at 06:51
1

That's type semantics. You decide. It's up to you how you implement the move.

In general, the state should be the same as the one gained by using non-parametric constructor.

Btw. move makes only sense if you are storing a data block behind a pointer (or some other movable class).

Šimon Tóth
  • 33,420
  • 18
  • 94
  • 135
  • I'm asking as a user of these types, not as an implementor. "If I write a function templated on T, what operations do I know work for any T?" T can be anything from int to std::vector to, well, *anything* else. –  Oct 06 '10 at 13:26
  • @Roger: If you write a function template, then you have requirements on the types that can be passed. If the type doesn't meet the requirements its wrongly used. – Šimon Tóth Oct 06 '10 at 13:27
  • @Let: That's why I'm asking what requirements are guaranteed for any type. For example, see the answers pointing out moved-from objects can be moved-into ("move-assigned"). –  Oct 06 '10 at 13:32
  • @Roger Well, in that case its super simple. There are absolutely no guarantees. – Šimon Tóth Oct 06 '10 at 13:33
  • 1
    @Let: There is already a guarantee pointed out: you can move-assign into it. –  Oct 06 '10 at 13:35
  • @Roger No, its just that no-one actually understood what you are asking. – Šimon Tóth Oct 06 '10 at 13:36
  • I'm upvoting to counter the downvote. @Roger, you're asking what guarantees you have for an object whose type models MoveConstructible. The mere fact that you can apply `std::move` to your object does not imply that you can assign to it. – avakar Oct 21 '10 at 14:45
  • @avakar: See my comments on Henrik's answer. –  Oct 21 '10 at 18:32
  • @Roger, I don't understand, is my previous point incorrect? (I'm defending the answer because I think that given the question, the answer "you decide" and "there are no guarantees" is correct. Only now I realize that you might not have been the downvoter so my comment might have been misdirected.) – avakar Oct 22 '10 at 07:11
  • @avakar: Ah, you assuming I was the downvoter makes more sense, but "it's completely up to you" isn't helpful. MoveAssignable being required if you ever want to use a type with swap or containers, for example, is. "Let" said he misunderstood me; I thought he was going to delete this answer after posting the second. I was asking for "sane" requirements and guidance, which aren't here either. –  Oct 22 '10 at 18:28
1

It depends on the class code. If class doesn't have rvalue reference constructor and assignment operator, std::move is ignored. std::move doesn't move anything, it just allows to treat its argument as rvalue reference, if appropriate function is available.

Correctly written && constructor and operator= must leave parameter instance in some consistent state, like empty string, and object should be usable. If there is operator=, another object may be correctly assigned to such empty instance.

Edit.

Generally, std::move should be used to apply move semantics to variable which is not rvalue, but actually it is:

SomeClass::SomeClass(SomeClass&& v)
{
    // Inside of this function, v is not rvalue anymore. But I know that actually 
    // this is rvalue, and use std::move
    OtherFunction(std::move(v));
}

In this case, mininal requirement to v is that it should be able to die without problems.

When std::move is used for variable which is not actually rvalue reference, really, this variable usability may be undefined. For my own classes, I would ensure some kind of consistency for this case. For another classes - it depends on specific class implementation, but I would not apply std::move to objects which are not actually rvalue references. I really don't know how this is defined (and whether it is defined) in the standard.

Alex F
  • 39,172
  • 34
  • 138
  • 200
  • I'm asking what the exact requirements for that consistent state are. The way I understand things currently, after func(std::move(obj));, you are not guaranteed obj.method() can be called, unless you know exactly what T is and know it guarantees that to be okay. –  Oct 06 '10 at 13:13
  • @Roger Of course you are allowed to call `obj.method();` The object doesn't go anywhere. It just changed state. – Šimon Tóth Oct 06 '10 at 13:21
  • @Let: By "you are not guaranteed obj.method() can be called", I mean "you are not guaranteed obj.method() isn't UB.". I realize the syntax is well-formed. –  Oct 06 '10 at 13:23
  • @Roger I have no idea what UB is but anyway, if the move operator gets the class into a state where you can't call methods from public interface it's just not implemented correctly. – Šimon Tóth Oct 06 '10 at 13:26
  • @Roger You are writing moving code, and it is your responsibility to keep object in usable state. Object which resources are "stolen" should behave as default empty object. If this is impossible, object methods must throw some known and documented exception or return failure (like ObjectDisposedException in .NET). This is what I would do writing class with && operators. However, I don't know whether standard requires this. – Alex F Oct 06 '10 at 13:28
  • @Let: [Undefined Behavior](http://stackoverflow.com/questions/2046952/limit-the-confusion-caused-by-undefined-behavior/2047172#2047172) –  Oct 06 '10 at 13:30
  • @Alex Why make your class movable if you can't move? – Šimon Tóth Oct 06 '10 at 13:30
  • @Alex: I'm not writing moving code, I'm writing a function templated on T which moves objects. T can be anything from int to std::vector to, well, *anything* else. "If this is impossible, object methods must throw some known..." – where is this stated, or is it just your advice? (It sounds like good advice, but it's not as strong as I'm looking for.) –  Oct 06 '10 at 13:30
  • @Roger Well, its definitely not UB. You could call it IB (but I only use that when talking about compiler, not user code). – Šimon Tóth Oct 06 '10 at 13:32
  • @Let: What guarantees it's not UB? Do you know any section in the 0x FCD or WD relating to this? –  Oct 06 '10 at 13:34
  • @Roger Because that wouldn't make logical sense. When the standard allows you to implement something, that alone means its not UB. As I already said you could call it IB. – Šimon Tóth Oct 06 '10 at 13:43
  • @Let: I can't understand what you're saying. –  Oct 06 '10 at 13:47
  • @Alex: The edit is wrong. Calling move on local variables is very much part of the reason it exists and not UB. –  Oct 06 '10 at 13:48
  • @Roger You should probably read something about the rvalue references, you seem to have no idea what they are: http://www.artima.com/cppsource/rvalue.html – Šimon Tóth Oct 06 '10 at 13:52
  • @Roger Pate I didn't say that using move on local variables is UB (UB - I like it!). But incorrect class implementation may cause UB. std::move is done to apply it to rvalue references, which are formally not rvalue references, as shown in my code. Applying them to local variables is side effect. – Alex F Oct 06 '10 at 13:59
  • @Alex: Then I don't understand what you mean by "When std::move is used for variable which is not actually rvalue reference, really, this variable usability may be undefined." –  Oct 06 '10 at 14:01
  • @Alex Well, if you use it on something that isn't a rvalue reference its the same as calling a function that takes a lvalue reference on something that isn't a lvalue reference. – Šimon Tóth Oct 06 '10 at 14:17
0

As I finally understood from the comments. You should check this: http://www.boost.org/doc/libs/1_44_0/libs/concept_check/concept_check.htm

This will allow you to check the type supplied as template parameters for concepts (features of the type). I'm not sure if they already have one for movable.

Šimon Tóth
  • 33,420
  • 18
  • 94
  • 135