ABu
ABu

Reputation: 12279

Is it serializing object representations by memcpy without creating objects safe as far as you don't directly access the values it contains?

#include <cstdlib>
#include <cstring>
#include <iostream>

// C++03 only.
int main()
{
    std::allocator<unsigned char> alloc;

    double d = 8;
    unsigned char* buf = alloc.allocate(sizeof(double));
    std::memcpy(buf, &d, sizeof(double));

    double extract;
    std::memcpy(&extract, buf, sizeof(double));

    std::cout << extract << '\n';

    alloc.deallocate(buf, sizeof(double));
}

I've created storage for an array of bytes that I've never brought to life by initializing it, so its lifetime never started (and in C++03 that is basically impossible to do since the correct way of doing it is by placement new but for arrays it adds a memory offset that you don't know what is it)

But, I don't actually need to "directly read" the data that is in this storage, I do all of this copy-paste of objects by means of std::memcpy calls.

My doubt is with the second std::memcpy call. In terms of value access, is it considered that the std::memcpy is reading the value of each buf array element so this array must be alive?

NOTE: It has been suggested that, this question is a possible clone of Can memcpy be used for type punning?. That other question is about C, and this question is not about type punning either, although it's related (I wrote originally a wrong question title because I misundertood what type punning actually means). I'm asking here about the precise semantics of the reading-from object that is passed to memcpy, whether that object must be alive or isn't actually required.

Upvotes: 1

Views: 516

Answers (1)

M.M
M.M

Reputation: 141618

The std::allocator<unsigned char>::allocate is specified as calling ::operator new (C++03 lib.allocator.members/3). So this question is substantially similar to "constructing" a trivially-copyable object with memcpy , although without the attempt to alias a value afterwards.

If we replaced the call to memcpy with a char assignment loop: unsigned char *p = (unsigned char *)&d; for (int i = 0; i < sizeof d; ++i) buf[i] = p[i]; then it is definitely undefined behaviour, since the assignment operator only has defined behaviour when the left hand side refers to an object that exists. See this answer for more detail.

However for the memcpy version, the question is: is memcpy the same as this char assignment loop, or something else?

The C++03 standard only defines memcpy by deferring to ISO C90, which says:

The memcpy function copies n characters from the object pointed to by s2 into the object pointed to by s1.

But it is unclear how to interpret this, since C has a different object model to C++. In C "object" means storage, whereas in C++ "object" and "storage" mean different things, and in the code in this question there is storage with no objects.

The answer by Shafik Yaghmour therefore describes the situation as "unspecified", although I think "not specified" or "unclear" would be better descriptions, since the term "unspecified" has a specific meaning in C++.

Footnote: Nothing substantial changed on this topic as of C++17. But in C++20 this will be well-defined, accepted proposal detail.

Upvotes: 2

Related Questions