См. ниже код:
unsigned char* p = new unsigned char[x];
CLASS* t = new (p) CLASS;
assert((void*)t == (void*)p);
Можно ли считать (void*)t == (void*)p
?
См. ниже код:
unsigned char* p = new unsigned char[x];
CLASS* t = new (p) CLASS;
assert((void*)t == (void*)p);
Можно ли считать (void*)t == (void*)p
?
Да, вы можете. Я считаю, что это гарантировано несколькими положениями.
[expr.new]/10 - Акцент на мой
Новое выражение передает объем пространства, запрошенный для функция распределения как первый аргумент типа std:: size_t. Это аргумент должен быть не меньше размера создаваемого объекта; он может быть больше размера создаваемого объекта, только если объект представляет собой массив. Для массивов char и unsigned char, разница между результатом нового выражения и адресом возвращаемый функцией распределения, должен быть целым кратным строжайшее требование основного выравнивания ([basic.align]) любого тип объекта, размер которого не превышает размер массива, создано. [Примечание: поскольку предполагается, что функции распределения возвращаются указатели на хранилище, которые соответствующим образом выровнены для объектов любого тип с фундаментальным выравниванием, это ограничение на распределение массива служебные данные позволяют распространять идиому выделения массивов символов в какие объекты других типов будут позже размещены. - конечная нота]
Что мне читает, как новое выражение должно создать объект (предполагая его не типа массива) по точному адресу, возвращаемому функцией распределения. Поскольку вы используете встроенное размещение new, это приведет нас к следующему
Эти функции зарезервированы, программа на С++ может не определять функции которые заменяют версии в стандартной библиотеке С++ ([ограничения]). Положения ([basic.stc.dynamic]) не применяются к этим зарезервированным формам размещения оператора new и оператора delete.
void* operator new(std::size_t size, void* ptr) noexcept;
Возвращает: ptr.
Примечания: Преднамеренно не выполняет никаких других действий.
Что гарантирует адрес, который вы передаете в выражение, является точным адресом выделенного вами массива символов. Это потому, что преобразования в void*
не меняют адрес источника.
Я думаю, это достаточно, чтобы обещать, что адреса одинаковы, даже если указатели не являются взаимозаменяемыми в целом. Итак, согласно [expr.eq]/1 (спасибо @T.C.):
Два указателя одного типа сравнивают одинаковые, если и только если они оба нулевые, оба указывают на одну и ту же функцию, или оба представляют одинаковые адрес ([basic.compound]).
Сравнение должно иметь значение true, опять же, потому что адреса одинаковы.
Можно ли считать
(void*)t == (void*)p
?
Не обязательно.
Если автор класса перегружает CLASS::operator new(size_t, unsigned char*)
, например, этот оператор может возвращать что-либо, кроме второго аргумента, например:
struct CLASS
{
static void* operator new(size_t, unsigned char* p) { return p + 1; }
};
Если вы хотите, чтобы это новое выражение вызывало стандартный оператор размещения без размещения места размещения, код должен
<new>
.void*
.::
для обхода CLASS::operator new
, если таковой имеется.например:.
#include <new>
#include <cassert>
unsigned char p[sizeof(CLASS)];
CLASS* t = ::new (static_cast<void*>(p)) CLASS;
assert(t == static_cast<void*>(p));
В этом случае t == static_cast<void*>(p)
действительно.
Это, на самом деле, то, что делает стандартная библиотека GNU С++:
template<typename _T1, typename... _Args>
inline void _Construct(_T1* __p, _Args&&... __args) {
::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...);
}