std::start_lifetime_as
cannot be implemented fully by hand, because it has the special property that it doesn't access the storage. Any implementation we can provide ourselves will have to access the storage in theory, even if this can be optimized out in practice.
However, disregarding this detail, we can implement it as follows:
C++20 - Implementation in terms of std::memmove
Since C++20, std::memmove
and std::memcpy
are "magic" in the sense that they implicitly begin the lifetimes of objects at the destination.
std::memmove
can have the same source and destination, so we can hijack its magic properties and implement std::start_lifetime_as
easily:
template<class T>
requires (std::is_trivially_copyable_v<T> && std::is_implicit_lifetime_v<T>)
T* start_lifetime_as(void* p) noexcept
{
return std::launder(static_cast<T*>(std::memmove(p, p, sizeof(T))));
}
The reason why this works is that:
Both functions [std::memcpy
and std::memmove
] implicitly create objects in the destination region of storage immediately prior to copying the sequence of characters to the destination.
- [cstring.syn] p3
std::memmove
turns the memory region [p, p + sizeof(T))
into one where an object is implicitly created. You may ask what the type of that object is:
For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types in its specified region of storage if doing so would result in the program having defined behavior.
- [intro.object] p10
Without std::launder
, it may be possible that objects of a type other than T
are created in this memory region. However, std::launder
has the precondition that there must be a T
at p
(see [ptr.launder] p2), so the compiler must create T
there to satisfy the former paragraph.
C++17 - Implementation in terms of placement new
std::start_lifetime_as
is implementable even without std::memmove
being "magic", and P0593R6 explains exactly how to do it.
The explanation in that paper predates std::memmove
being given magic properties, which is why it suggests a more complicated implementation:
If the destination type is a trivially-copyable implicit-lifetime type, this can be accomplished by copying the storage elsewhere, using placement new of an array of byte-like type, and copying the storage back to its original location, then using std::launder
to acquire a pointer to the newly-created object.
- §3.8 Direct object creation
template<class T>
requires (std::is_trivially_copyable_v<T> && std::is_implicit_lifetime_v<T>)
T* start_lifetime_as(void* p) noexcept
{
// 1. Copy the storage elsewhere.
std::byte backup[sizeof(T)];
std::memcpy(backup, p, sizeof(T));
// 2. Use placement new of an array of byte-like type
// according to [intro.objec] p13, this implicitly begins the lifetime
// of an object within the byte storage.
// However, it also turns the memory at p indeterminate.
new (p) std::byte[sizeof(T)];
// 3. Copy the storage back to its original location.
// This turns the object representation determinate again,
// while keeping the implicit object creation at p.
std::memcpy(p, backup, sizeof(T));
// 4. Return a laundered pointer.
// Because T being at the address that p represents is a
// precondition of std::launder, this forces the implicit
// object created via placement new to be of type T.
return std::launder(static_cast<T*>(p));
}
Note 1: Your implementation has the problem that the memory turns indeterminate when you do placement new of a std::byte[]
, so the object representation won't be preserved.
Note 2: The C++17 version isn't complete, because it doesn't allow for starting the lifetime of volatile
types.
T
would have undefined value here, you need to recopy previous value of buffer. – Sacristanprocess(Stream *stream)
. previous value might have importance. – Sacristanstart_lifetime_as()
as preserving an existing object representation over the new lifetime start. So yes, there is more to it and I'm curious whether a C++20 compiler already allows for an efficient implementation ofstd::start_lifetime_as()
. – Shadshadberry