In future standards of C++, we will have the concept of "trivial relocatability", which means we can simply copy bytes from one object to an uninitialized chunk of memory, and simply ignore/zero out the bytes of the original object.
this way, we imitate the C-style way of copying/moving objects around.
In future standards, we will probably have something like std::is_trivially_relocatable<type> as a type trait. currently, the closest thing we have is std::is_pod<type> which will be deprecated in C++20.
My question is, do we have a way in the current standard (C++17) to figure out if the object is trivially relocatable?
For example, std::unique_ptr<type> can be moved around by copying its bytes to a new memory address and zeroing out the original bytes, but std::is_pod_v<std::unique_ptr<int>> is false.
Also, currently the standard mandate that every uninitialized chunk of memory must pass through a constructor in order to be considered a valid C++ object. even if we can somehow figure out if the object is trivially relocatable, if we just move the bytes - it's still UB according to the standard.
So another question is - even if we can detect trivial relocatability, how can we implement trivial relocation without causing UB? simply calling memcpy + memset(src,0,...) and casting the memory address to the right type is UB.
`
Thanks!
The whole point of trivial-relocatability would seem to be to enable byte-wise moving of objects even in the presence of a non-trivial move constructor or move assignment operator. Even in the current proposal P1144R3, this ultimately requires that a user manually mark types for which this is possible. For a compiler to figure out whether a given type is trivially-relocatable in general is most-likely equivalent to solving the halting problem (it would have to understand and reason about what an arbitrary, potentially user-defined move constructor or move assignment operator does)…
It is, of course, possible that you define your own is_trivially_relocatable trait that defaults to std::is_trivially_copyable_v and have the user specialize for types that should specifically be considered trivially-relocatable. Even this is problematic, however, because there's gonna be no way to automatically propagate this property to types that are composed of trivially-relocatable types…
Even for trivially-copyable types, you can't just copy the bytes of the object representation to some random memory location and cast the address to a pointer to the type of the original object. Since an object was never created, that pointer will not point to an object. And attempting to access the object that pointer doesn't point to will result in undefined behavior. Trivial-copyabibility means you can copy the bytes of the object representation from one existing object to another existing object and rely on that making the value of the one object equal to the value of the other [basic.types]/3.
To do this for trivially-relocating some object would mean that you have to first construct an object of the given type at your target location, then copy the bytes of the original object into that, and then modify the original object in a way equivalent to what would have happened if you had moved from that object. Which is essentially a complicated way of just moving the object…
There's a reason a proposal to add the concept of trivial-relocatability to the language exists: because you currently just can't do it from within the langugage itself…
Note that, despite all this, just because the compiler frontend cannot avoid generating constructor calls doesn't mean the optimizer cannot eliminate unnecessary loads and stores. Let's have a look at what code the compiler generates for your example of moving a std::vector or std::unique_ptr:
auto test1(void* dest, std::vector<int>& src)
{
return new (dest) std::vector<int>(std::move(src));
}
auto test2(void* dest, std::unique_ptr<int>& src)
{
return new (dest) std::unique_ptr<int>(std::move(src));
}
As you can see, just doing an actual move often already boils down to just copying and overwriting some bytes, even for non-trivial types…
Author of P1144 here; somehow I'm just seeing this SO question now!
std::is_trivially_relocatable<T> is proposed for some-future-version-of-C++, but I don't predict it'll get in anytime soon (definitely not C++23, I bet not C++26, quite possibly not ever). The paper (P1144R6, June 2022) ought to answer a lot of your questions, especially the ones where people are correctly answering that if you could already implement this in present-day C++, we wouldn't need a proposal. See also my 2019 C++Now talk.
Michael Kenzel's answer says that P1144 "ultimately requires that a user manually mark types for which [trivial relocation] is possible"; I want to point out that that's kind of the opposite of the point. The state of the art for trivial relocatability is manual marking ("warranting") of each and every such type; for example, in Folly, you'd say
struct Widget {
std::string s;
std::vector<int> v;
};
FOLLY_ASSUME_FBVECTOR_COMPATIBLE(Widget);
And this is a problem, because the average industry programmer shouldn't be bothered with trying to figure out if std::string is trivially relocatable on their library of choice. (The annotation above is wrong on 1.5 of the big 3 vendors!) Even Folly's own maintainers can't get these manual annotations right 100% of the time.
So the idea of P1144 is that the compiler can just take care of it for you. Your job changes from dangerously warranting things-you-don't-necessarily-know, to merely (and optionally) verifying things-you-want-to-be-true via static_assert (Godbolt):
struct Widget {
std::string s;
std::vector<int> v;
};
static_assert(std::is_trivially_relocatable_v<Widget>);
struct Gadget {
std::string s;
std::list<int> v;
};
static_assert(!std::is_trivially_relocatable_v<Gadget>);
In your (OP's) specific use-case, it sounds like you need to find out whether a given lambda type is trivially relocatable (Godbolt):
void f(std::list<int> v) {
auto widget = [&]() { return v; };
auto gadget = [=]() { return v; };
static_assert(std::is_trivially_relocatable_v<decltype(widget)>);
static_assert(!std::is_trivially_relocatable_v<decltype(gadget)>);
}
This is something you can't really do at all with Folly/BSL/EASTL, because their warranting mechanisms work only on named types at the global scope. You can't exactly FOLLY_ASSUME_FBVECTOR_COMPATIBLE(decltype(widget)).
Inside a std::function-like type, you're correct that it would be useful to know whether the captured type is trivially relocatable or not. But since you can't know that, the next best thing (and what you should do in practice) is to check std::is_trivially_copyable. That's the currently blessed type trait that literally means "This type is safe to memcpy, safe to skip the destructor of" — basically all the things you're going to be doing with it. Even if you knew that the type was exactly std::unique_ptr<int>, or whatever, it would still be undefined behavior to memcpy it in present-day C++, because the current standard says that you're not allowed to memcpy types that aren't trivially copyable.
(Btw, technically, P1144 doesn't change that fact. P1144 merely says that the implementation is allowed to elide the effects of relocation, which is a huge wink-and-nod to implementors that they should just use memcpy. But even P1144R6 doesn't make it legal for ordinary non-implementor programmers to memcpy non-trivially-copyable types: it leaves the door open for some compiler to implement, and some library implementation to use, a __builtin_trivial_relocate function that is in some magical sense distinguishable from a plain old memcpy.)
Finally, your last paragraph refers to memcpy + memset(src,0,...). That's wrong. Trivial relocation is tantamount to just memcpy. If you care about the state of the source object afterward — if you care that it's all-zero-bytes, for example — then that must mean you're going to look at it again, which means you aren't actually treating it as destroyed, which means you aren't actually doing the semantics of a relocate here. "Copy and null out the source" is more often the semantics of a move. The point of relocation is to avoid that extra work.
Related
I've just read
What is the purpose of std::launder?
and frankly, I am left scratching my head.
Let's start with the second example in #NicolBolas' accepted answer:
aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));
[basic.life]/8 tells us that, if you allocate a new object in the
storage of the old one, you cannot access the new object through
pointers to the old. std::launder allows us to side-step that.
So, why not just change the language standard so that accessing data through a reinterpret_cast<int*>(&data) is valid/appropriate? In real life, money laundering is a way to hide reality from the law. But we don't have anything to hide - we're doing something perfectly legitimate here. So why can't the compiler just change it's behavior to its std::launder() behavior when it notices we're accessing data this way?
On to the first example:
X *p = new (&u.x) X {2};
Because X is trivial, we need not destroy the old object before
creating a new one in its place, so this is perfectly legal code. The
new object will have its n member be 2.
So tell me... what will u.x.n return?
The obvious answer will be 2. But that's wrong, because the compiler
is allowed to assume that a truly const variable (not merely a const&,
but an object variable declared const) will never change. But we just
changed it.
So why not make the compiler not be allowed to make the assumption when we write this kind of code, accessing the constant field through the pointer?
Why is it reasonable to have this pseudo-function for punching a hole in formal language semantics, rather than setting the semantics to what they need to be depending on whether or not the code does something like in these examples?
depending on whether or not the code does something like in these examples
Because the compiler cannot always know when data is being accessed "this way".
As things currently stand, the compiler is allowed to assume that, for the following code:
struct foo{ int const x; };
void some_func(foo*);
int bar() {
foo f { 123 };
some_func(&f);
return f.x;
}
bar will always return 123. The compiler may generate code that actually accesses the object. But the object model does not require this. f.x is a const object (not a reference/pointer to const), and therefore it cannot be changed. And f is required to always name the same object (indeed, these are the parts of the standard you would have to change). Therefore, the value of f.x cannot be changed by any non-UB means.
Why is it reasonable to have this pseudo-function for punching a hole in formal language semantics
This was actually discussed. That paper brings up how long these issues have existed (ie: since C++03) and often optimizations made possible by this object model have been employed.
The proposal was rejected on the grounds that it would not actually fix the problem. From this trip report:
However, during discussion it came to light that the proposed alternative would not handle all affected scenarios (particularly scenarios where vtable pointers are in play), and it did not gain consensus.
The report doesn't go into any particular detail on the matter, and the discussions in question are not publicly available. But the proposal itself does point out that it wouldn't allow devirtualizing a second virtual function call, as the first call may have build a new object. So even P0532 would not make launder unnecessary, merely less necessary.
I take the opposite approach. I don't like that c++ just plays around with memory like this or at least allows you to. The compiler should throw an error when trying to create a new object and an address that contains a constant value or an object but has a const member. If the programmer says const it's const. Sorry.
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)
Given the following code:
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_t;
//this moves the back of src to the back of dst:
void push_popped(std::list<storage_t> & dstLst, std::list<storage_t> & srcLst)
{
auto & src = srcLst.back();
dstLst.push_back(storage_t());
auto & dst = dstLst.back();
std::memcpy(&dst, &src, sizeof(T));
srcLst.pop_back();
}
I'm aware of 3 reasons why this approach is not, in general, correct (even though it avoids calling src->~T() and so avoids double-reclamation of T's resources).
object members of type U* that point to other U members of the same object
hidden class members may need to be updated (vtable, for instance)
the system needs to record that no T exists anymore at src and that a T does now exist at dst
(These are mentioned here: http://www.gamedev.net/topic/655730-c-stdmove-vs-stdmemcpy/#entry5148523.)
Assuming that T is not a type whose memory address is a property of its state (std::mutex or std::condition_variable, for instance), are these the only issues with this approach? Or are there other things that could go wrong? I'd like a description of the unknown issues.
I'd like to think I have an "object relocation semantics" developed, but I'd rather not ask people to consider it if there's an obvious hole in it.
The concept of "trivially copyable" implies that a memcpy is safe. You can test if a type is trivially copyable via a trait in std.
It includes the idea that destroying it is a noop; in your case, you want destruction to not be a noop, but rather not done on the source, while being done on the dest.
The concept of "move-and-destroy-source" has been proposed in the C++1z standardization process independent of the "trivially copyable" concept. It was proposed for exception safety; there are types for which a move-construct is not exception-safe, but a move-construct-and-destroy-source would be. And there are thorny problems involving exceptions and container allocations that make a noexcept move-ctor operation very valuable.
If that gets into the standard, then a trivially-copyable-if-you-don't-destroy-source concept could also be added to the standard, if it proves valuable.
It wouldn't apply to everything move semantics can enhance, and it may require effort on the part of programmers (how the compiler can work out that "it is ok to elide a destroyer" is not going to be easy; all non-trivial non-structural properties of Turing Machine behavior are intractable.)
Why use Copy constructor instead of std::memcpy ?
The Move constructor/move assignment oeprator gives you encapsulated opratunity to do some other usefull stuff when you move an object - logging, cleaning up, etc.
preformance wise - in many cases the compiler can optimize many moves to just one (imagin many functions that simply returns some object from one another). with memcpy their abilty is much more restricted.
and finally - because C++ is not about moving bytes around - it's about using objects as a basis for your program.
Reading the Eigen library documentation, I noticed that some objects cannot be passed by value. Are there any developments in C++11 or planned developments that will make it safe to pass such objects by value?
Also, why is there no problem with returning such objects by value?
It is entirely possible that Eigen is just a terribly written library (or just poorly-thought out); just because something is online doesn't make it true. For example:
Passing objects by value is almost always a very bad idea in C++, as this means useless copies, and one should pass them by reference instead.
This is not good advice in general, depending on the object. It is sometimes necessary pre-C++11 (because you might want an object to be uncopyable), but in C++11, it is never necessary. You might still do it, but it is never necessary to always pass a value by reference. You can just move it by value if it contains allocated memory or something. Obviously, if it's a "look-but-don't-touch" sort of thing, const& is fine.
Simple struct objects, presumably like Eigen's Vector2d are probably cheap enough to copy (especially in x86-64, where pointers are 64-bits) that the copy won't mean much in terms of performance. At the same time, it is overhead (theoretically), so if you're in performance critical code, it may help.
Then again, it may not.
The particular crash issue that Eigen seems to be talking about has to do with alignment of objects. However, most C++03 compiler-specific alignment support guarantees that alignment in all cases. So there's no reason that should "make your program crash!". I've never seen an SSE/AltaVec/etc-based library that used compiler-specific alignment declarations that caused crashes with value parameters. And I've used quite a few.
So if they're having some kind of crash problem with this, then I would consider Eigen to be of... dubious merit. Not without further investigation.
Also, if an object is unsafe to pass by value, as the Eigen docs suggest, then the proper way to handle this would be to make the object non-copy-constructable. Copy assignment would be fine, since it requires an already existing object. However, Eigen doesn't do this, which again suggests that the developers missed some of the finer points of API design.
However, for the record, C++11 has the alignas keyword, which is a standard way to declare that an object shall be of a certain alignment.
Also, why is there no problem with returning such objects by value?
Who says that there isn't (noting the copying problem, not the alignment problem)? The difference is that you can't return a temporary value by reference. So they're not doing it because it's not possible.
They could do this in C++11:
class alignas(16) Matrix4f
{
// ...
};
Now the class will always be aligned on a 16-byte boundary.
Also, maybe I'm being silly but this shouldn't be an issue anyway. Given a class like this:
class Matrix4f
{
public:
// ...
private:
// their data type (aligned however they decided in that library):
aligned_data_type data;
// or in C++11
alignas(16) float data[16];
};
Compilers are now obligated to allocate a Matrix4f on a 16-byte boundary anyway, because that would break it; the class-level alignas should be redundant. But I've been known to be wrong in the past, somehow.
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.