Consider the following type:
struct S
{
char v;
};
Given an array of const S
, is it possible to, in a standard conformant way, reinterpret it as an array of const char
whose elements correspond to the value of the member v
for each of the original array's elements, and vice-versa? For example:
const S a1[] = { {'a'}, {'4'}, {'2'}, {'\0'} };
const char* a2 = reinterpret_cast< const char* >(a1);
for (int i = 0; i < 4; ++i)
std::cout << std::boolalpha << (a1[i].v == a2[i]) << ' ';
Is the code above portable and would it print true true true true
? If not, is there any other way of achieving this?
Obviously, it is possible to create a new array and initialize it with the member v
of each element of the original array, but the whole idea is to avoid creating a new array.
Trivially, no - the struct
may have padding. And that flat out breaks any reinterpretation as an array.
Formally the struct
may have padding so that its size is greater than 1.
I.e., formally you can't reinterpret_cast
and have fully portable code, except for ¹an array of only one item.
But for the in-practice, some years ago someone asked if there was now any compiler that by default would give sizeof(T) > 1
for struct T{ char x; };
. I have yet to see any example. So in practice one can just static_assert
that the size is 1, and not worry at all that this static_assert
will fail on some system.
I.e.,
S const a1[] = { {'a'}, {'4'}, {'2'}, {'\0'} };
static_assert( sizeof( S ) == 1, "!" );
char const* const a2 = reinterpret_cast<char const*>( a1 );
for( int i = 0; i < 4; ++i )
{
assert( a1[i].v == a2[i] );
}
Since it's possible to interpret the C++14 and later standards in a way where the indexing has Undefined Behavior, based on a peculiar interpretation of "array" as referring to some original array, one might instead write this code in a more awkward and verbose but guaranteed valid way:
// I do not recommend this, but it's one way to avoid problems with some compiler that's
// based on an unreasonable, impractical interpretation of the C++14 standard.
#include <assert.h>
#include <new>
auto main() -> int
{
struct S
{
char v;
};
int const compiler_specific_overhead = 0; // Redefine per compiler.
// With value 0 for the overhead the internal workings here, what happens
// in the machine code, is the same as /without/ this verbose work-around
// for one impractical interpretation of the standard.
int const n = 4;
static_assert( sizeof( S ) == 1, "!" );
char storage[n + compiler_specific_overhead];
S* const a1 = ::new( storage ) S[n];
assert( (void*)a1 == storage + compiler_specific_overhead );
for( int i = 0; i < n; ++i ) { a1[i].v = "a42"[i]; } // Whatever
// Here a2 points to items of the original `char` array, hence no indexing
// UB even with impractical interpretation of the C++14 standard.
// Note that the indexing-UB-free code from this point, is exactly the same
// source code as the first code example that some claim has indexing UB.
char const* const a2 = reinterpret_cast<char const*>( a1 );
for( int i = 0; i < n; ++i )
{
assert( a1[i].v == a2[i] );
}
}
Notes:
¹ The standard guarantees that there's no padding at the start of the struct
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With