Please see the below code:
unsigned char* p = new unsigned char[x];
CLASS* t = new (p) CLASS;
assert((void*)t == (void*)p);
Can I assume (void*)t == (void*)p
?
Please see the below code:
unsigned char* p = new unsigned char[x];
CLASS* t = new (p) CLASS;
assert((void*)t == (void*)p);
Can I assume (void*)t == (void*)p
?
Yes you may. I believe it's guaranteed by several provisions.
[expr.new]/10 - Emphasis mine
A new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array. For arrays of char and unsigned char, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement ([basic.align]) of any object type whose size is no greater than the size of the array being created. [ Note: Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating character arrays into which objects of other types will later be placed. — end note ]
Which to me reads like the new expression must create an object (assuming it's not of array type) at the exact address returned by the allocation function. Since you are using the built-in placement new, this take us to the following
These functions are reserved, a C++ program may not define functions that displace the versions in the Standard C++ library ([constraints]). The provisions of ([basic.stc.dynamic]) do not apply to these reserved placement forms of operator new and operator delete.
void* operator new(std::size_t size, void* ptr) noexcept;
Returns: ptr.
Remarks: Intentionally performs no other action.
Which guarantees the address you pass to the expression is the exact address of the character array object you allocated. That's because conversions to void*
do not change the source address.
I think it's enough to promise the addresses are the same, even if the pointers are not interchangeable in general. So according to [expr.eq]/1 (thanks to @T.C.):
Two pointers of the same type compare equal if and only if they are both null, both point to the same function, or both represent the same address ([basic.compound]).
The comparison must yield true, again because the addresses are the same.
Can I assume
(void*)t == (void*)p
?
Not necessarily.
If the author of the class overloads CLASS::operator new(size_t, unsigned char*)
, for example, that operator can return anything other than the second argument, e.g.:
struct CLASS
{
static void* operator new(size_t, unsigned char* p) { return p + 1; }
};
If you would like this new expression to call the standard non-allocating placement new operator the code needs to
<new>
.void*
argument.::
to bypass CLASS::operator new
, if any.E.g.:
#include <new>
#include <cassert>
unsigned char p[sizeof(CLASS)];
CLASS* t = ::new (static_cast<void*>(p)) CLASS;
assert(t == static_cast<void*>(p));
In this case t == static_cast<void*>(p)
indeed.
This is, in fact, what GNU C++ standard library does:
template<typename _T1, typename... _Args>
inline void _Construct(_T1* __p, _Args&&... __args) {
::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...);
}