I would like to define a class for marshalling data; when marshalling is finished, I would like to move
the marshalled data out from within it, which will probably invalidate the marshalling object.
I believe this is possible with the static
function extractData
below:
class Marshaller
{
public:
static DataType extractData(Marshaller&& marshaller)
{
return std::move(marshaller.data);
}
private:
DataType data;
}
This is a bit inconvenient to call, though:
Marshaller marshaller;
// ... do some marshalling...
DataType marshalled_data{Marshaller::extractData(std::move(marshaller))};
So can I wrap it with a member function?
DataType Marshaller::toDataType()
{
return Marshaller::extractData(std::move(*this));
}
This would, of course, be called using:
DataType marshalled_data{marshaller.toDataType()};
...which, to me, looks much nicer. But that std::move(*this)
thing looks awfully suspicious. In the context of the call to toDataType()
, marshaller
can't be used again, but I don't think the compiler can know that: the body of the function could be outside the caller's compilation unit, so there's nothing to indicate that marshaller
has had move()
applied to it.
Is this undefined behavior? Is it perfectly fine? Or somewhere in between? Is there a nicer way to accomplish the same goal, preferably without using a macro or requiring the caller to explicitly move
marshaller
?
EDIT: With both G++ and Clang++, I found that not only could I compile the above use case, but I could actually continue to make modifications to the underlying data via the marshaller, then re-extract the modified data using the toDataType
function. I also found that the already-extracted data in marshalled_data
continued to be changed by marshaller
, which indicates that the marshalled_data
is shared between the marshaller
and the calling context, so I suspect that there is either a memory-leak or undefined behavior (from double-deletion) here.
EDIT 2: If I put a print statement in DataType
's destructor, it appears twice when the caller leaves scope. If I include a data member in DataType
that has an array in it, with a corresponding new[]
and delete[]
, I get a glibc
"double free or corruption" error. So I'm not sure how this could be safe, even though several answers have said that it's technically allowed. A complete answer should explain what is required to use this technique correctly with a non-trivial DataType
class.
EDIT 3: This is enough of a rabbit-hole/can-of-worms that I've opened up another question to address my remaining concerns.