9

Is the following legal C++ with well-defined behaviour?

class my_class { ... };

int main()
{
    char storage[sizeof(my_class)];
    new ((void *)storage) my_class();
}

Or is this problematic because of pointer casting/alignment considerations?

bluescarni
  • 3,937
  • 1
  • 22
  • 33
  • For me, it is just fine. – Neigyl R. Noval Jan 03 '11 at 08:59
  • 2
    No, the ellipses are not legal in that context...(Hint: if you want to ask if code is well-defined, it has to compile first.) – GManNickG Jan 03 '11 at 09:05
  • Any one please tell me what is the usage of doing above thing in real pogramming world. – vrbilgi Jan 03 '11 at 09:07
  • 3
    @GMan: I really hope you're just joking. Yes, the well-definedness depends on the omitted section, but it's obvious what the question is. – Jon Purdy Jan 03 '11 at 09:09
  • @Jon: No, not really joking. It's best if the question is clear, without ambiguity. (And no, whether it's well-defined does *not* depend on the omitted section; you simply have no such guarantee.) – GManNickG Jan 03 '11 at 09:11
  • i'd just love to know why anyone would want to do this – David Heffernan Jan 03 '11 at 09:13
  • @user430294 @David Heffernan: Herb Sutter wrote a [GotW article (#28)](http://www.gotw.ca/gotw/028.htm) about a pimpl idiom that uses something like what the OP wrote. Then he recommends against it because the array is not guaranteed to be aligned, unlike dynamic memory. In short: don't do it. – In silico Jan 03 '11 at 09:17
  • 2
    @user430294 @David Heffernan: the goal would basically be to allocate the storage for an object but construct the object only as necessary. And at the same time preserving a cache-friendly memory access pattern (so no dynamic allocation). – bluescarni Jan 03 '11 at 09:52
  • @bluescarni right, 'cos it wouldn't do to use dynamic allocation! – David Heffernan Jan 03 '11 at 19:39

5 Answers5

15

Yes, it's problematic. You simply have no guarantee that the memory is properly aligned.

While various tricks exist to get storage with proper alignment, you're best off using Boost's or C++0x's aligned_storage, which hide these tricks from you.

Then you just need:

// C++0x
typedef std::aligned_storage<sizeof(my_class),
                                alignof(my_class)>::type storage_type;

// Boost
typedef boost::aligned_storage<sizeof(my_class),
                        boost::alignment_of<my_class>::value>::type storage_type;

storage_type storage; // properly aligned
new (&storage) my_class(); // okay

Note that in C++0x, using attributes, you can just do this:

char storage [[align(my_class)]] [sizeof(my_class)];
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Sorry, not quite there yet :) As far as I've understood, the storage type is some kind of builtin integer type. How do I use it? Can I just cast freely to the my_class type via void *? – bluescarni Jan 03 '11 at 10:31
  • Sorry, I meant to say: "cast freely back and forth to/from pointers to my_class instances"... Or something like that :) – bluescarni Jan 03 '11 at 10:35
  • @bluescarni: Ah, sorry. My answer is incomplete: the resulting type should be used as a block of memory, so it should be the address of that storage. I've fixed it. – GManNickG Jan 03 '11 at 10:36
3

As people have mentioned here, this won't necessarily work due to alignment restrictions. There are several ways to get the alignment right. First, if you have a C++0x-compliant compiler, you can use the alignof operator to try to force the alignment to be correct. Second, you could dynamically-allocate the character array, since memory from operator new is guaranteed to be aligned in such a way that anything can use it correctly. Third, you could try storing the character array in a union with some type that has the maximum possible alignment on your system; I believe that this article has some info on it (though it's designed for C++03 and is certainly not as good as the alignof operator that's coming out soon).

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
2

In case anyone wants to avoid Boost or C++1x, this complete code works both in GCC and MSVC. The MSVC-specific code is based on Chromium's aligned_memory.h. It's a little more complex than the GCC version, because MSVC's __declspec(align(.)) only accepts literal alignment values, and this is worked around using template specialization for all possible alignments.

#ifdef _MSC_VER

template <size_t Size, size_t Align>
struct AlignedMemory;

#define DECLARE_ONE_ALIGNED_MEMORY(alignment) \
template <size_t Size> \
struct __declspec(align(alignment)) AlignedMemory<Size, alignment> { \
    char mem[Size]; \
};

DECLARE_ONE_ALIGNED_MEMORY(1)
DECLARE_ONE_ALIGNED_MEMORY(2)
DECLARE_ONE_ALIGNED_MEMORY(4)
DECLARE_ONE_ALIGNED_MEMORY(8)
DECLARE_ONE_ALIGNED_MEMORY(16)
DECLARE_ONE_ALIGNED_MEMORY(32)
DECLARE_ONE_ALIGNED_MEMORY(64)
DECLARE_ONE_ALIGNED_MEMORY(128)
DECLARE_ONE_ALIGNED_MEMORY(256)
DECLARE_ONE_ALIGNED_MEMORY(512)
DECLARE_ONE_ALIGNED_MEMORY(1024)
DECLARE_ONE_ALIGNED_MEMORY(2048)
DECLARE_ONE_ALIGNED_MEMORY(4096)

#else

template <size_t Size, size_t Align>
struct AlignedMemory {
    char mem[Size];
} __attribute__((aligned(Align)));

#endif

template <class T>
struct AlignedMemoryFor : public AlignedMemory<sizeof(T), __alignof(T)> {};
Ambroz Bizjak
  • 7,809
  • 1
  • 38
  • 49
2

It is at least problematic due to alignment.

On most Non-Intel architecture the code will generate a "bus error" due to wrong alignment or be extremely slow because of processor traps needed to fix the unaligned memory access.

On Intel architecture this will normally just be a bit slower than usual. Except if some SSE operations are involved, then it may also crash.

Markus Kull
  • 1,471
  • 13
  • 16
0

The char array may not be aligned correctly for the size of myclass. On some architectures, that means slower accesses, and on others, it means a crash. Instead of char, you should use a type whose alignment is equal to or greater than that of the struct, which is given by the largest alignment requirement of any of its members.

#include <stdint.h>

class my_class { int x; };

int main() {
    uint32_t storage[size];
    new(storage) my_class();
}

To allocate enough memory for one my_class instance, I think size ought to be sizeof(my_class) / sizeof(T), where T is whichever type you use to get the correct alignment.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • "...whose alignment is equal to or greater than that of the struct, which is given by the largest alignment requirement of any of its members" I don't think this is guaranteed, alignment is totally implementation-defined. (That is, it's alignment isn't guaranteed to necessarily be the alignment of strictest member.) Also, `int` could be greater than 32-bits. – GManNickG Jan 03 '11 at 09:24
  • @GMan: You're right. It works in most cases, though. Here, have a link: http://stackoverflow.com/questions/364483/determining-the-alignment-of-c-c-structures-in-relation-to-its-members – Jon Purdy Jan 03 '11 at 10:13