Consider the following C++11 code:
#include <type_traits>
struct bar
{
virtual void do_bar() const {}
};
struct foo
{
std::aligned_storage<sizeof(bar),alignof(bar)>::type m_storage;
};
bar
is not standard layout because of the virtual function do_bar()
. However, foo
is standard layout as the type provided by std::aligned_storage
is a POD type and foo
satisfies all the other requirements for standard layout types.
What happens then when I use the m_storage
storage with placement new
to construct an instance of bar
? E.g.,
foo f;
::new(static_cast<void *>(&f.m_storage)) bar();
Is this legal? Can I use this to cheat my way around restrictions about standard layout types?
Alignment helps the CPU fetch data from memory in an efficient manner: less cache miss/flush, less bus transactions etc. Some memory types (e.g. RDRAM, DRAM etc.) need to be accessed in a structured manner (aligned "words" and in "burst transactions" i.e. many words at one time) in order to yield efficient results.
64-bit aligned is 8 bytes aligned). A memory access is said to be aligned when the data being accessed is n bytes long and the datum address is n-byte aligned. When a memory access is not aligned, it is said to be misaligned. Note that by definition byte memory accesses are always aligned.
struct aligned_storage; (since C++11) (deprecated in C++23) Provides the nested type type , which is a trivial standard-layout type suitable for use as uninitialized storage for any object whose size is at most Len and whose alignment requirement is a divisor of Align .
Data alignment: Data alignment means putting the data in memory at an address equal to some multiple of the word size. This increases the performance of the system due to the way the CPU handles memory.
Here's your code again:
struct bar {
virtual void do_bar() const {}
};
struct foo {
std::aligned_storage<sizeof(bar), alignof(bar)>::type m_storage;
};
This is fine. struct foo
is a standard-layout type, and, given an instance foo myFoo
, you can construct an object of type bar
into myFoo.m_storage
.
However, this is completely pointless from the compiler's POV, so why bother with it? As @dyp wisely said in the comments, "Why do you want foo to be standard-layout?"
You handwaved something about unions. Well, that's fine. You can write this:
union DoesntWork {
bar b; // compiler error in C++11 due to non-standard-layout type
int i;
};
union DoesWork {
foo f; // works fine in C++11, of course
int i;
};
However, equally obviously, you cannot expect this to work:
struct car {
int initialsequence;
};
struct bar {
int initialsequence;
virtual void do_bar() const {}
};
struct foo {
std::aligned_storage<sizeof(bar), alignof(bar)>::type m_storage;
bar& asBar() { return *reinterpret_cast<bar*>(&m_storage); }
};
union JustSilly {
foo f;
car c;
} js;
assert(&js.c.initialsequence == // Fails, because no matter how many
&js.f.asBar().initialsequence); // casts you add, bar still has a vptr!
In other words, you're free to lie to the compiler (via type-punning and reinterpret_cast), but that doesn't make your lies true. ;)
See also: XY problem.
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