This question already has answers here:
Closed 10 years ago.
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;
}
...
};
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.
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 intrinsically "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.)
Related
I made a class for a function's argument to delegate its validation and also for function overloading purposes.
Throwing from constructor guarantees that the object will either be constructed in a valid state or will not be constructed at all. Hence, there is no need to introduce any checking member functions like explicit operator bool() const.
// just for exposition
const auto &certString = open_cert();
add_certificate(cert_pem{certString.cbegin(), certString.cend()}); // this will either throw
// or add a valid certificate.
// cert_pem is a temporary
However, there are issues which I don't see a appealing solution for:
Argument-validation class might itself be made non-persistent - to be used only for validation as a temporary object. But what about classes that are allowed to be persistent? That is living after function invocation:
// just for exposition
const auto &certString = open_cert();
cert_pem cert{certString.cbegin(), certString.cend()}; // allowed to throw
cert_pem moved = std::move(cert); // cert invalidated
cert_pem cert_invalid = std::move(cert); // is not allowed to throw
add_certificate(cert_invalid); // we lost the whole purpoce
I can see several ways to treat this without introducing state-checking (thus declaring a class stateful) functions:
Declare object "unusable" after move. - A really simple recipe for disaster
Declare move constructor and assignment operator deleted. Allow only copy - Resources might be very expensive to copy. Or even not possible if using a PIMPL idiom.
Use heap allocation when need an object to be persistent - this looks like most obvious. But has an unnecessary penalty on performance. Especially when some class has as members several such objects - there will be several memory allocations upon construction.
Here is a code example for 2):
/**
* Class that contains PEM certificate byte array.
* To be used as an argument. Ensures that input certificate is valid, otherwise throws on construction.
*/
class cert_pem final
{
public:
template <typename IterT>
cert_pem(IterT begin, IterT end)
: value_(begin, end)
{
validate(value_);
}
const std::vector<uint8_t>& Value() const noexcept(false)
{
return value_;
}
cert_pem (const cert_pem &) = default;
cert_pem & operator=(const cert_pem &) = default;
cert_pem (cert_pem &&) = delete;
cert_pem & operator=(cert_pem &&) = delete;
private:
/**
* \throws std::invalid_argument
*/
static void Validate(const std::vector<uint8_t>& value) noexcept(false);
static void ValidateNotEmpty(const std::vector<uint8_t>& value) noexcept(false);
private:
std::vector<uint8_t> value_;
};
Is there another way to handle this problem without these shortcomings? Or will I have to choose one of the above?
I think that with argument-validating classes a good way would be to not allow it to be persistent - only temporary object is allowed. But I am not sure if it is possible in C++.
You are trying to maintain two invariants at once, and their semantics are in conflict. The first invariant is the validity of the certificate. The second is for memory management.
For the first invariant, you decided that there can be no invalid constructed object, but for the second, you decided that the object can be either valid or unspecified†. This is only possible because the deallocation has a check somewhere.
There is no way around this: you either add a check for the first or you decouple the invariants. One way of decoupling them is to follow the design of std::lock_guard
cert c = open_cert(); // c is guaranteed to not have memory leaks and is movable
{
cert_guard cg{c}; // cg is guaranteed to be valid, but cg is non-movable
}
But wait, you might ask, how do you transfer the validity to another cert_guard?
Well, you can't.
That is the semantics you chose for the first invariant: it is valid exactly during the lifetime of the object. That is the entire point.
† Unspecified and invalid as far as the certificate is concerned.
The question aims to design a type such that:
an object of the type always satisfies a given invariant
an object of the type is "usable" as a non-temporary
The question then makes a leap from (2) to ask that the type be movable. But it need not be: copy and move operations could be defined as deleted. The question fails to motivate why the move operations are necessary. If that is a need, it comes from an unstated requirement. A non-movable class can be emplaced in a map, returned from a function, and used in many other ways. It admittedly can be more painful to use, but it can be used.
So that's one option that's not listed: define copy and move operations as deleted.
Otherwise, let's assume we do want:
an object of the type always satisfies a given invariant
the type is movable
This is not in conflict. Every copyable class is movable, and copying is a valid strategy here. Remember that move operations allow a "potentially smarter" copy, by allowing the source to be mutated. There are still two C++ objects, and it is still a logical copy, but with an assumption that the source won't be needed anymore in its current state (so you can steal from it!). There is no difference in the C++ interface, only in the totally unchecked documented behavior of the type after a move operation.
Defining move operations as deleted gives you a copyable class. This is your second option listed. Assigning from an xvalue (cert_pem moved = std::move(cert)) will still compile, but will not invalidate the source. It will still be considered movable by the language. The trade-off is as you note, copies can be expensive. Note that PIMPL authors can give their types copy operations, that's a choice they make about what the interface of the type should be, and the idiom doesn't prevent it.
The third choice is a version of the second. By putting values behind a shared_ptr, one can make an expensive-to-copy type cheap to copy. But we still rely on copy as the strategy for move.
The first choice amounts to weakening the invariant in (1). A moved-from object satisfying a different set of invariants than a normal object is very typical in C++. It is annoying, but in many cases it is the best we can do. When only one object can exist satisfying the invariant (think: non-null unique_ptr) the moved-from object must violate it.
The accepted answer amounts to my first option combined with delayed construction: define copy and move operations as deleted. Creating the guard can throw if the object was moved-from. The guard is just the type maintaining the invariant, and it is non-movable. We can delay its construction because such types are difficult to manage. We do that by keeping an object that knows enough about how to construct it. This strategy exists in other forms (emplace functions and piecewise_construct constructors to construct objects in their eventual place, factory functions to construct the object at will, etc.).
However, the description in the accepted answer leaves a bit to be desired, in my opinion. The desire is to maintain the invariant while being movable (this is assumed). Being movable doesn't require that the moved-from object satisfy an invariant or be unspecified. That's a choice the author of the type makes, and what choices are available is exactly the question, by my reading of it. Although the example given only implicated memory, and the first answer mentioned memory, my reading of the question was more general: maintaining invariants in movable classes.
Knowing that all copyable classes are movable, that move is a "smart" copy, and that there are two objects in and after a move operation will help in understanding why there's such limited options here. One has to leave that source object in some state.
My advice is to embrace the radioactive moved-from object. That's the approach in the standard library, and defaulted move operations will obey that more often than not. For such types, there must be some "empty" state for moved-from objects, so all types are effectively optional and a default constructor can also be defined to get an object in that empty state.
Have found comparable questions but not exactly with such a case.
Take the following code for example:
#include <iostream>
#include <string>
#include <vector>
struct Inner
{
int a, b;
};
struct Outer
{
Inner inner;
};
std::vector<Inner> vec;
int main()
{
Outer * an_outer = new Outer;
vec.push_back(std::move(an_outer->inner));
delete an_outer;
}
Is this safe? Even if those were polymorphic classes or ones with custom destructors?
My concern regards the instance of "Outer" which has a member variable "inner" moved away. From what I learned, moved things should not be touched anymore. However does that include the delete call that is applied to outer and would technically call delete on inner as well (and thus "touch" it)?
Neither std::move, nor move semantics more generally, have any effect on the object model. They don't stop objects from existing, nor prevent you from using those objects in the future.
What they do is ask to borrow encapsulated resources from the thing you're "moving from". For example, a vector, which directly only stores a pointer some dynamically-allocated data: the concept of ownership of that data can be "stolen" by simply copying that pointer then telling the vector to null the pointer and never have anything to do with that data again. It's yielded. The data belongs to you now. You have the last pointer to it that exists in the universe.
All of this is achieved simply by a bunch of hacks. The first is std::move, which just casts your vector expression to vector&&, so when you pass the result of it to a construction or assignment operation, the version that takes vector&& (the move constructor, or move-assignment operator) is triggered instead of the one that takes const vector&, and that version performs the steps necessary to do what I described in the previous paragraph.
(For other types that we make, we traditionally keep following that pattern, because that's how we can have nice things and persuade people to use our libraries.)
But then you can still use the vector! You can "touch" it. What exactly you can do with it is discoverable from the documentation for vector, and this extends to any other moveable type: the constraints emplaced on your usage of a moved-from object depend entirely on its type, and on the decisions made by the person who designed that type.
None of this has any impact on the lifetime of the vector. It still exists, it still takes memory, and it will still be destructed when the time comes. (In this particular example you can actually .clear() it and start again adding data to a new buffer.)
So, even if ints had any sort of concept of this (they don't; they encapsulate no indirectly-stored data, and own no resources; they have no constructors, so they also have no constructors taking int&&), the delete "touch"ing them would be entirely safe. And, more generally, none of this depends on whether the thing you've moved from is a member or not.
More generally, if you had a type T, and an object of that type, and you moved from it, and one of the constraints for T was that you couldn't delete it after moving from it, that would be a bug in T. That would be a serious mistake by the author of T. Your objects all need to be destructible. The mistake could manifest as a compilation failure or, more likely, undefined behaviour, depending on what exactly the bug was.
tl;dr: Yes, this is safe, for several reasons.
std::move is a cast to an rvalue-reference, which primarily changes which constructor/assignment operator overload is chosen. In your example the move-constructor is the default generated move-constructor, which just copies the ints over so nothing happens.
Whether or not this generally safe depends on the way your classes implement move construction/assignment. Assume for example that your class instead held a pointer. You would have to set that to nullptr in the moved-from class to avoid destroying the pointed-to data, if the moved-from class is destroyed.
Because just defining move-semantics is a custom way almost always leads to problems, the rule of five says that if you customize any of:
the copy constructor
the copy assignment operator
the move constructor
the move assignment operator
the destructor
you should usually customize all to ensure that they behave consistently with the expectations a caller would usually have for your class.
An example move constructor implementation from a C++ course I’m taking looks a bit like this:
/// Move constructor
Motorcycle::Motorcycle(Motorcycle&& ori)
: m_wheels(std::move(ori.m_wheels)),
m_speed(ori.m_speed),
m_direction(ori.m_direction)
{
ori.m_wheels = array<Wheel, 2>();
ori.m_speed = 0.0;
ori.m_direction = 0.0;
}
(m_wheels is a member of type std::array<Wheel, 2>, and Wheel only contains a double m_speed and a bool m_rotating. In the Motorcycle class, m_speed and m_direction are also doubles.)
I don’t quite understand why ori’s values need to be cleared.
If a Motorcycle had any pointer members we wanted to “steal”, then sure, we’d have to set ori.m_thingy = nullptr so as to not, for example, delete m_thingy twice. But does it matter when the fields contain the objects themselves?
I asked a friend about this, and they pointed me towards this page, which says:
Move constructors typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state. For example, moving from a std::string or from a std::vector may result in the argument being left empty. However, this behaviour should not be relied upon.
Who defines what indeterminate state means? I don’t see how setting the speed to 0.0 is any more indeterminate than leaving it be. And like the final sentence in the quote says — code shouldn’t rely on the state of the moved-from Motorcycle anyway, so why bother cleaning it up?
They don't need to be cleaned. The designer of the class decided it would be a good idea to leave the moved-from object zero initialized.
Note that a situation where is does make sense is for objects managing resources that get released in the destructor. For instance, a pointer to dynamically allocated memory. Leaving the pointer in the moved-from object unmodified would mean two objects managing the same resource. Both their destructors would attempt to release.
Who defines what indeterminate state means?
The author of the class.
If you look at the documentation for std::unique_ptr, you'll see that after the following lines:
std::unique_ptr<int> pi = std::make_unique<int>(5);
auto pi2 = std::move(pi);
pi is actually in a very defined state. It will have been reset() and pi.get() will be equal to nullptr.
You're very probably correct that the author of this class is putting unnecessary operations in the constructor.
Even if m_wheels were a heap-allocated type, such as std::vector, there would still be no reason to "clear" it, since it is already being passed to its own move-constructor:
: m_wheels(std::move(ori.m_wheels)), // invokes move-constructor of appropriate type
However, you have not shown enough of the class to permit us to know whether the explicit "clearing" operations are necessary in this particular case. As per Deduplicator's answer, the following functions are expected to behave correctly in the "moved-from" state:
// Destruction
~Motorcycle(); // This is the REALLY important one, of course
// Assignment
operator=(const Motorcycle&);
operator=(Motorcycle&&);
Therefore, you must look at the implementation of each of these functions in order to determine whether the move-constructor is correct.
If all three use the default implementation (which, from what you've shown, seems reasonable), then there's no reason to manually clear the moved-from objects. However, if any of these functions use the values of m_wheels, m_speed, or m_direction to determine how to behave, then the move-constructor may need to clear them in order to ensure the correct behavior.
Such a class design would be inelegant and error-prone, since typically we would not expect the values of primitives to matter in the "moved-from" state, unless the primitives are explicitly used as flags to indicate whether clean-up must occur in the destructor.
Ultimately, as an example for use in a C++ class, the move-constructor shown is not technically "wrong" in the sense of causing undesired behavior, but it seems like a poor example because it is likely to cause exactly the confusion that led you to post this question.
There are few things which must be safely doable with a moved-from object:
Destruct it.
Assign / Move to it.
Any other operations which explicitly say so.
So, you should move from them as fast as possible while giving those few guarantees, and more only if they are useful and you can fulfill them for free.
Which often means not clearing the source, and other times implies doing it.
As an addition, prefer explicitly defaulted over user-defined functions, and implicitly defined ones over both, for brevity and preserving triviality.
There is no standard behavior for this. Like with pointer you can still use them after you deleted them. Some people see you should not care and just not reuse the object but the compiler won't prohibit that.
Here is one blogpost (not by me) about this with some interesting discussion in the comment:
https://foonathan.github.io/blog/2016/07/23/move-safety.html
and the follow up:
https://foonathan.github.io/blog/2016/08/24/move-default-ctor.html
And here with also a recent Video chat about this topic with arguments discussing this:
https://www.youtube.com/watch?v=g5RnXRGar1w
Basically it's about treating a moved-from object like a deleted pointer or making it in a safe to be "moved-from-state"
Who defines what indeterminate state means?
The English language, I think. "Indeterminate" here has one of its usual English meanings, "Not determinate; not precisely fixed in extent; indefinite; uncertain. Not established. Not settled or decided". The state of a moved-from object is not constrained by the standard other than that it must be "valid" for that object type. It need not be the same every time an object is moved from.
If we were only talking about types provided by the implementation, then the proper language would be a "valid but otherwise unspecified state". But we reserve the word "unspecified" for talking about details of the C++ implementation, not details of what user code is allowed to do.
"Valid" is defined separately for each type. Taking integer types as an example, trap representations are not valid and anything that represents a value is valid.
That standard here is not saying that the move constructor must make the object be indeterminate, merely that it needn't put it into any determined state. So although you're correct that 0 is not "more indeterminate" than the old value, it is in any case moot since the move constructor needn't make the old object "as indeterminate as possible".
In this case, the author of the class has chosen to put the old object into one specific state. It's then entirely up to them whether they document what that state is, and if they do then it's entirely up to users of the code whether they rely on it.
I would usually recommend not relying on it, because under certain circumstances code that you write thinking of it semantically being a move, actually does a copy. For example, you put std::move on the right-hand side of an assignment not caring whether the object is const or not because it works either way, and then somebody else comes along and thinks "ah, it's been moved from, it must have been cleared to zeros". Nope. They've overlooked that Motorcycle is cleared when moved from, but const Motorcycle of course is not, no matter what the documentation might suggest to them :-)
If you're going to set a state at all, then it's really a coin toss which state. You could set it to a "clear" state, perhaps matching what the no-args constructor does (if there is one), on the basis that this represents the most neutral value there is. And I suppose on many architectures 0 is the (perhaps joint-)cheapest value to set something to. Alternatively you could set it to some eye-catcher value, in the hope that when someone writes a bug where they accidentally move from an object and then use its value, they'll think to themselves "What? The speed of light? In a residential street? Oh yeah, I remember, that's the value this class sets when moved-from, I probably did that".
The source object is an rvalue, potentially an xvalue. So the thing one needs to be concerned with is the imminent destruction of that object.
Resource handles or pointers are the most significant item that distinguishes a move from a copy: after the operation the ownership of that handle is assumed to be transferred.
So obviously, as you mention in the question, during a move we need to affect the source object so that it no-longer identifies itself as owner of transferred objects.
Thing::Thing(Thing&& rhs)
: unqptr_(move(rhs.unqptr_))
, rawptr_(rhs.rawptr_)
, ownsraw_(rhs.ownsraw_)
{
the.ownsraw_ = false;
}
Note that I don't clear rawptr_.
Here a design decision is made that, as long as the ownership flag is only true for one object, we don't care if there is a dangling pointer.
However, another engineer might decide that the pointer should be cleared so that instead of random ub, the following mistake results in a nullptr access:
void MyClass::f(Thing&& t) {
t_ = move(t);
// ...
cout << t;
}
In the case of something as innocuous as the variables shown in the question, they may not need to be cleared but that depends on the class design.
Consider:
class Motorcycle
{
float speed_ = 0.;
static size_t s_inMotion = 0;
public:
Motorcycle() = default;
Motorcycle(Motorcycle&& rhs);
Motorcycle& operator=(Motorcycle&& rhs);
void setSpeed(float speed)
{
if (speed && !speed_)
s_inMotion++;
else if (!speed && speed_)
s_inMotion--;
speed_ = speed;
}
~Motorcycle()
{
setSpeed(0);
}
};
This is a fairly artificial demonstration that ownership isn't necessarily a simple pointer or bool, but can be an issue of internal consistency.
The move operator could use setSpeed to populate itself, or it could just do
Motorcycle::Motorcycle(Motorcycle&& rhs)
: speed_(rhs.speed_)
{
rhs.speed_ = 0; // without this, s_inMotion gets confused
}
(Apologies for typos or autocorrects, typed on my phone)
What is the standard way to do it?
Let's have a class A that has a data member std::unique_ptr<Impl> m_impl.
For example, should class A's move assignment operator's contents look like this?
m_impl = std::move(other.m_impl);
Or this?
*m_impl = std::move(*other.m_impl);
The first case would be much more efficient than the second. But it would raise a few issues.
After moving, the m_impl data member of other is nullptr. Should the moved-from object throw exceptions when it's being used or just let the user run into runtime errors due to dereferencing a nullptr?
For thread-safe classes this would also mean that all m_impl usage needs to be synchronized. I am not sure if calling std::unique_ptr's move constructor/assignment operator is thread-safe or not.
The former. When you are using a unique_ptr, you are probably doing it because the object itself probably isn't copyable and/or movable in the first place. So moving the pointee is usually not an option.
After moving, the m_impl data member of other is nullptr.
That is correct moved-from std::unique_ptr. It needs to support destruction and reassignment and everything else is undefined and does not matter what it does (segfault in case of null pointer).
For thread-safe classes this would also mean that all m_impl usage needs to be synchronized.
Standard library is not thread-safe unless explicitly stated, only reentrant. If you object needs to be thread-safe, you have to ensure it yourself.
That said if a thread moves an object of which it is not a sole owner, it will cause problems for the other threads, because they will still try to access it at the old location.
You could lock the move operations against all operations on the object and define semantics of the operations on the moved-from object to return error in some way, but it sounds like big pain and seriously confusing to use.
Thread-safe objects should really be exception. You should aim for handing objects over between threads so that only one owns the object at any given time and to have all shared data immutable (except possible reference count, which can be done with reasonably performant std::atomic or std::shared_ptr, which is built on top of it).
If you really need a thread-safe object, it should either not be movable, or should behave like handle referring to it's internals by std::shared_ptr, which is thread-safe.
Let's see this with light from a different topic.
How do you swap pimpl class objects?
void swap(T& x, T& y) {
std::swap(x.m_impl, y.m_impl);
}
Right?
It only moves around the m_impl pointer. So, with move semantics, you should also only move the m_impl pointer!
m_impl = std::move(other.m_impl);
However, as an effect, users of your class should not dereference m_impl in a moved-from object. This is something move semantics introduces when you use it in your classes. It is up to the users of your class to prevent this from happening, just as your code should not do v[0] if v is a moved-from std::vector, etc.
A moved-from object is not supposed to be "alive" anymore (valid but unspecified) valid but unspecified:
Destruction allowed
Re-Assignment allowed
Other use disallowed
The first option, i.e. moving the member (i.e. ones on innards) and not the referencee, is preferable.
A problem of "value types" with external resources (like std::vector<T> or std::string) is that copying them tends to be quite expensive, and copies are created implicitly in various contexts, so this tends to be a performance concern. C++0x's answer to this problem is move semantics, which is conceptionally based on the idea of resource pilfering and technically powered by rvalue references.
Does D have anything similar to move semantics or rvalue references?
I believe that there are several places in D (such as returning structs) that D manages to make them moves whereas C++ would make them a copy. IIRC, the compiler will do a move rather than a copy in any case where it can determine that a copy isn't needed, so struct copying is going to happen less in D than in C++. And of course, since classes are references, they don't have the problem at all.
But regardless, copy construction already works differently in D than in C++. Generally, instead of declaring a copy constructor, you declare a postblit constructor: this(this). It does a full memcpy before this(this) is called, and you only make whatever changes are necessary to ensure that the new struct is separate from the original (such as doing a deep copy of member variables where needed), as opposed to creating an entirely new constructor that must copy everything. So, the general approach is already a bit different from C++. It's also generally agreed upon that structs should not have expensive postblit constructors - copying structs should be cheap - so it's less of an issue than it would be in C++. Objects which would be expensive to copy are generally either classes or structs with reference or COW semantics.
Containers are generally reference types (in Phobos, they're structs rather than classes, since they don't need polymorphism, but copying them does not copy their contents, so they're still reference types), so copying them around is not expensive like it would be in C++.
There may very well be cases in D where it could use something similar to a move constructor, but in general, D has been designed in such a way as to reduce the problems that C++ has with copying objects around, so it's nowhere near the problem that it is in C++.
I think all answers completely failed to answer the original question.
First, as stated above, the question is only relevant for structs. Classes have no meaningful move. Also stated above, for structs, a certain amount of move will happen automatically by the compiler under certain conditions.
If you wish to get control over the move operations, here's what you have to do. You can disable copying by annotating this(this) with #disable. Next, you can override C++'s constructor(constructor &&that) by defining this(Struct that). Likewise, you can override the assign with opAssign(Struct that). In both cases, you need to make sure that you destroy the values of that.
For assignment, since you also need to destroy the old value of this, the simplest way is to swap them. An implementation of C++'s unique_ptr would, therefore, look something like this:
struct UniquePtr(T) {
private T* ptr = null;
#disable this(this); // This disables both copy construction and opAssign
// The obvious constructor, destructor and accessor
this(T* ptr) {
if(ptr !is null)
this.ptr = ptr;
}
~this() {
freeMemory(ptr);
}
inout(T)* get() inout {
return ptr;
}
// Move operations
this(UniquePtr!T that) {
this.ptr = that.ptr;
that.ptr = null;
}
ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that"
swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary
return this;
}
}
Edit:
Notice I did not define opAssign(ref UniquePtr!T that). That is the copy assignment operator, and if you try to define it, the compiler will error out because you declared, in the #disable line, that you have no such thing.
D have separate value and object semantics :
if you declare your type as struct, it will have value semantic by default
if you declare your type as class, it will have object semantic.
Now, assuming you don't manage the memory yourself, as it's the default case in D - using a garbage collector - you have to understand that object of types declared as class are automatically pointers (or "reference" if you prefer) to the real object, not the real object itself.
So, when passing vectors around in D, what you pass is the reference/pointer. Automatically. No copy involved (other than the copy of the reference).
That's why D, C#, Java and other language don't "need" moving semantic (as most types are object semantic and are manipulated by reference, not by copy).
Maybe they could implement it, I'm not sure. But would they really get performance boost as in C++? By nature, it don't seem likely.
I somehow have the feeling that actually the rvalue references and the whole concept of "move semantics" is a consequence that it's normal in C++ to create local, "temporary" stack objects. In D and most GC languages, it's most common to have objects on the heap, and then there's no overhead with having a temporary object copied (or moved) several times when returning it through a call stack - so there's no need for a mechanism to avoid that overhead too.
In D (and most GC languages) a class object is never copied implicitly and you're only passing the reference around most of the time, so this may mean that you don't need any rvalue references for them.
OTOH, struct objects are NOT supposed to be "handles to resources", but simple value types behaving similar to builtin types - so again, no reason for any move semantics here, IMHO.
This would yield a conclusion - D doesn't have rvalue refs because it doesn't need them.
However, I haven't used rvalue references in practice, I've only had a read on them, so I might have skipped some actual use cases of this feature. Please treat this post as a bunch of thoughts on the matter which hopefully would be helpful for you, not as a reliable judgement.
I think if you need the source to loose the resource you might be in trouble. However being GC'ed you can often avoid needing to worry about multiple owners so it might not be an issue for most cases.