2

I'm implementing a very strange structure in which I allocate a single memory chunk and store multiple objects of different type in it:

auto memory = reinterpret_cast<std::uintptr_t>(::operator new(size));
new(reinterpret_cast<void*>(memory)) Class1();
new(reinterpret_cast<void*>(memory + offset)) Class2();

My concern, does this code violates the strict aliasing rules?

What if I rewrite this code as follows:

void* memory = ::operator new(size);
new(memory) Class1();
new(reinterpret_cast<void*>(reinterpret_cast<char*>(memory) + offset)) Class2();

Given that size is guaranteed to be big enough, memory + offset is guaranteed to be properly aligned, both class constructors declared nothrow, and destructors of both classes are called upon memory release, does this code introduces UB? What other problems I can encounter with such code?

Zelta
  • 754
  • 5
  • 14
  • 2
    Assuming you mean multiple objects (not classes), then this is not strange at all - you are implementing a memory pool. – Lightness Races in Orbit Feb 25 '18 at 15:40
  • I store multiple objects of different type. Don't know how to formulate the title correctly. – Zelta Feb 25 '18 at 15:42
  • 2
    It's fine. Don't see a problem with the question atm. To actually answer it properly, I'd have to delve into the standard more deeply than I care to on a Sunday, but I hope you get what you're looking for. Just want to reassure you that this is a perfectly normal thing to want to do (arguably, it's why placement new exists in the first place). – Lightness Races in Orbit Feb 25 '18 at 16:42

1 Answers1

2

In answering your question

My concern, does this code violates the strict aliasing rules?

No it doesn't

Let's understand something first.

What is aliasing exactly?

Aliasing is when more than one lvalue refers to the same memory location (when you hear lvalue, think of things (variables) that can be on the left-hand side of assignments), i.e. that are modifiable. As an example:

int anint;
int *intptr=&anint;

Why was aliasing rules even introduced in the first place?

Before strict aliasing was introduced, the compiler had to live in a state of paranoia that the contents of buff could change at anytime from anywhere by anybody. So to get an extra performance edge, and assuming most people don't type-pun pointers, the strict aliasing rule was introduced.

So in this kind of setup, if I want to send a message to something I'd have to have two incompatible pointers pointing to the same chunk of memory.

As @Lightness mention correctly

arguably, it's why placement new exists in the first place

Placement new allows you to construct an object on memory that's already allocated. And you may want to do this for optimizations (it is faster not to re-allocate all the time) but you need to re-construct an object multiple times. If you need to keep re-allocating it might be more efficient to allocate more than you need, even though you don't want to use it yet.

What other problems I can encounter with such code? And What if I rewrite this code as follows:

 void* memory = ::operator new(size);
    new(memory) Class1();
    new(reinterpret_cast<void*>(reinterpret_cast<char*>(memory) + offset)) Class2();

Be rest assured that compiler will flag you some warnings.

Note: In order to discover aliasing problems as quickly as possible, -fstrict-aliasing should always be included in the compilation flags for GCC. Otherwise problems may only be visible at the highest optimization levels where it is the most difficult to debug.

You might want to have a look on Endianness, Understanding C/C++ Strict Aliasing and What uses are there for “placement new”?

antzshrek
  • 9,276
  • 5
  • 26
  • 43