Casting a char array to an object pointer – is this UB?

  • A+
Category:Languages

I recently saw a class like this that was used to construct objects "on-demand" without having to use dynamic memory allocation for various reasons.

#include <cassert>  template<typename T> class StaticObject { public:     StaticObject() : constructed_(false)     {     }      ~StaticObject()     {         if (constructed_)             ((T*)object_)->~T();     }      void construct()     {         assert(!constructed_);          new ((T*)object_) T;         constructed_ = true;     }      T& operator*()     {         assert(constructed_);          return *((T*)object_);     }      const T& operator*() const     {         assert(constructed_);          return *((T*)object_);     }  private:     bool constructed_;     alignas(alignof(T)) char object_[sizeof(T)]; }; 

Is this code, namely the casting of a properly aligned char array to an object pointer, considered undefined behavior by the C++14 standard or is it completely fine?

 


Casting a char array to an object pointer - is this UB?

Casting one pointer (the array decays to a pointer) to another pointer that is not in same inheritance hierarchy using a C-style cast performs a reinterpret cast. A reinterpret cast itself never has UB.

However, indirecting a converted pointer can have UB if an object of appropriate type has not been constructed into that address. In this case, an object has been constructed in the character array, so the indirection has well defined behaviour. Edit: The indirection would be UB free, if it weren't for the strict aliasing rules; see ascheplers answer for details. aschepler shows a C++14 conforming solution. In C++17, your code can be corrected with following changes:

void construct() {     assert(!constructed_);     new (object_) T; // removed cast     constructed_ = true; }  T& operator*() {     assert(constructed_);     return *(std::launder((T*)object_)); } 

To construct an object into an array of another type, three requirements must be met to avoid UB: The other type must be allowed to alias the object type (char, unsigned char and std::byte satisfy this requirement for all object types), the address must be aligned to the memory boundary as required by the object type and none of the memory must overlap with the lifetime of another object (ignoring the underlying objects of the array which are allowed to alias the overlaid object). All of those requirements are satisfied by your program.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: