Let's consider following piece of code:
struct Blob {
double x, y, z;
} blob;
char* s = reinterpret_cast<char*>(&blob);
s[2] = 'A';
Assuming that sizeof(double) is 8, does this code trigger undefined behaviour?
Let's consider following piece of code:
struct Blob {
double x, y, z;
} blob;
char* s = reinterpret_cast<char*>(&blob);
s[2] = 'A';
Assuming that sizeof(double) is 8, does this code trigger undefined behaviour?
Quoting from N4140 (roughly C++14):
3.9 Types [basic.types]
2 For any object (other than a base-class subobject) of trivially copyable type
T
, whether or not the object holds a valid value of typeT
, the underlying bytes (1.7) making up the object can be copied into an array ofchar
orunsigned char
.42 If the content of the array ofchar
orunsigned char
is copied back into the object, the object shall subsequently hold its original value.42) By using, for example, the library functions (17.6.1.2)
std::memcpy
orstd::memmove
.3 For any trivially copyable type
T
, if two pointers toT
point to distinctT
objectsobj1
andobj2
, where neitherobj1
norobj2
is a base-class subobject, if the underlying bytes (1.7) making upobj1
are copied intoobj2
,43obj2
shall subsequently hold the same value asobj1
. [ Example: ... ]43) By using, for example, the library functions (17.6.1.2)
std::memcpy
orstd::memmove
.
This does, in principle, allow assignment directly to s[2]
if you take the position that assignment to s[2]
is indirectly required to be equivalent to copying all of some other Blob
into an array that just happens to be bytewise identical except for the third byte, and copying it into your Blob
: you're not assigning to s[0]
, s[1]
, etc. For trivially copyable types including char
, that is equivalent to setting them to the exact value they already have, which also has no observable effect.
However, if the only way to get s[2] == 'A'
is by memory manipulation, then a valid argument could also be made that what you're copying back into your Blob
isn't the underlying bytes that made up any previous Blob
. In that case, technically, the behaviour would be undefined by omission.
I do strongly suspect, especially given the "whether or not the object holds a valid value of type T
" comment, that it's intended to be allowed.
Chapter 3.10 of the standard seems to allow for that specific case, assuming that "access the stored value" means "read or write", which is unclear.
3.10-10
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
—(10.1) the dynamic type of the object,
—(10.2) a cv-qualified version of the dynamic type of the object,
—(10.3) a type similar (as defined in 4.4) to the dynamic type of the object,
—(10.4) a type that is the signed or unsigned type corresponding to the dynamic type of the object,
—(10.5) a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
—(10.6) an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
—(10.7) a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
—(10.8) a char or unsigned char type.