mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 22:50:38 +03:00
implement moveEmplace
- combined move and emplace - moves a value and emplaces it into an uninitialized value of the same type - more efficient than init + move
This commit is contained in:
parent
6681862b4e
commit
aefe7e5557
1 changed files with 81 additions and 41 deletions
|
@ -856,52 +856,16 @@ Params:
|
|||
*/
|
||||
void move(T)(ref T source, ref T target)
|
||||
{
|
||||
import core.stdc.string : memcpy, memset;
|
||||
import std.traits : hasAliasing, hasElaborateAssign,
|
||||
hasElaborateCopyConstructor, hasElaborateDestructor,
|
||||
isAssignable;
|
||||
|
||||
static if (!is( T == class) && hasAliasing!T) if (!__ctfe)
|
||||
{
|
||||
import std.exception : doesPointTo;
|
||||
assert(!doesPointTo(source, source), "Cannot move object with internal pointer.");
|
||||
}
|
||||
import std.traits : hasElaborateDestructor;
|
||||
|
||||
static if (is(T == struct))
|
||||
{
|
||||
if (&source == &target) return;
|
||||
// Most complicated case. Destroy whatever target had in it
|
||||
// and bitblast source over it
|
||||
static if (hasElaborateDestructor!T) typeid(T).destroy(&target);
|
||||
|
||||
static if (hasElaborateAssign!T || !isAssignable!T)
|
||||
memcpy(&target, &source, T.sizeof);
|
||||
else
|
||||
target = source;
|
||||
|
||||
// If the source defines a destructor or a postblit hook, we must obliterate the
|
||||
// object in order to avoid double freeing and undue aliasing
|
||||
static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T)
|
||||
{
|
||||
// If T is nested struct, keep original context pointer
|
||||
static if (__traits(isNested, T))
|
||||
enum sz = T.sizeof - (void*).sizeof;
|
||||
else
|
||||
enum sz = T.sizeof;
|
||||
|
||||
auto init = typeid(T).init();
|
||||
if (init.ptr is null) // null ptr means initialize to 0s
|
||||
memset(&source, 0, sz);
|
||||
else
|
||||
memcpy(&source, init.ptr, sz);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Primitive data (including pointers and arrays) or class -
|
||||
// assignment works great
|
||||
target = source;
|
||||
// Destroy target before overwriting it
|
||||
static if (hasElaborateDestructor!T) target.__xdtor();
|
||||
}
|
||||
// move and emplace source into target
|
||||
() @trusted { moveEmplace(source, target); }();
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1180,6 +1144,82 @@ unittest// Issue 8057
|
|||
static assert(__traits(compiles, move(x, x) ));
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to $(LREF move) but assumes `target` is uninitialized. This
|
||||
* is more efficient because `source` can be blitted over `target`
|
||||
* without destroying or initializing it first.
|
||||
*
|
||||
* Params:
|
||||
* source = value to be moved into target
|
||||
* target = uninitialized value to be filled by source
|
||||
*/
|
||||
void moveEmplace(T)(ref T source, ref T target) @system
|
||||
{
|
||||
import core.stdc.string : memcpy, memset;
|
||||
import std.traits : hasAliasing, hasElaborateAssign,
|
||||
hasElaborateCopyConstructor, hasElaborateDestructor,
|
||||
isAssignable;
|
||||
|
||||
static if (!is(T == class) && hasAliasing!T) if (!__ctfe)
|
||||
{
|
||||
import std.exception : doesPointTo;
|
||||
assert(!doesPointTo(source, source), "Cannot move object with internal pointer.");
|
||||
}
|
||||
|
||||
static if (is(T == struct))
|
||||
{
|
||||
assert(&source !is &target, "source and target must not be identical");
|
||||
|
||||
static if (hasElaborateAssign!T || !isAssignable!T)
|
||||
memcpy(&target, &source, T.sizeof);
|
||||
else
|
||||
target = source;
|
||||
|
||||
// If the source defines a destructor or a postblit hook, we must obliterate the
|
||||
// object in order to avoid double freeing and undue aliasing
|
||||
static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T)
|
||||
{
|
||||
// If T is nested struct, keep original context pointer
|
||||
static if (__traits(isNested, T))
|
||||
enum sz = T.sizeof - (void*).sizeof;
|
||||
else
|
||||
enum sz = T.sizeof;
|
||||
|
||||
auto init = typeid(T).init();
|
||||
if (init.ptr is null) // null ptr means initialize to 0s
|
||||
memset(&source, 0, sz);
|
||||
else
|
||||
memcpy(&source, init.ptr, sz);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Primitive data (including pointers and arrays) or class -
|
||||
// assignment works great
|
||||
target = source;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
static struct Foo
|
||||
{
|
||||
this(int* ptr) { _ptr = ptr; }
|
||||
~this() { if (_ptr) ++*_ptr; }
|
||||
int* _ptr;
|
||||
}
|
||||
|
||||
Foo foo1 = void; // uninitialized
|
||||
auto foo2 = Foo(new int); // initialized
|
||||
|
||||
// Using `move(foo2, foo1)` has an undefined effect because it destroys the uninitialized foo1.
|
||||
// MoveEmplace directly overwrites foo1 without destroying or initializing it first.
|
||||
assert(foo2._ptr !is null);
|
||||
moveEmplace(foo2, foo1);
|
||||
assert(foo1._ptr !is null && foo2._ptr is null);
|
||||
}
|
||||
|
||||
// moveAll
|
||||
/**
|
||||
For each element $(D a) in $(D src) and each element $(D b) in $(D
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue