Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aligned storage and standard layout

Tags:

c++

c++11

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?

like image 783
bluescarni Avatar asked Dec 12 '13 18:12

bluescarni


People also ask

Why is data alignment important?

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.

What is 64 bit alignment?

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.

What is std :: Aligned_storage?

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 .

What is alignment programming?

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.


1 Answers

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.

like image 178
Quuxplusone Avatar answered Sep 29 '22 20:09

Quuxplusone