Возвращаемое значение размещения new
Рассмотрим следующий код С++ 14:
#include <cassert>
#include <new>
#include <type_traits>
struct NonStandardLayout
{
// ...
};
int main()
{
using T = NonStandardLayout;
std::aligned_storage_t< sizeof(T), alignof(T) > storage;
T *const valid_ptr = new(static_cast<void *>(&storage)) T;
T *const maybe_ptr = reinterpret_cast<T *>(&storage);
assert(maybe_ptr == valid_ptr); // ???
valid_ptr->T::~T();
return 0;
}
Гарантируется ли это стандартом, что утверждение в примере никогда не завершится неудачно, для любого типа T?
Обсуждение
В последнем стандарте (http://eel.is/c++draft/), я не вижу ссылки на этот конкретный сценарий, но я нашел следующие параграфы, которые возможно, указывает на ответ "да".
Правильно ли мне думать, что
[Expr.new/15]
а также
[New.delete.placement/2]
вместе утверждает, что значение valid_ptr
будет равно адресу storage
, всегда?
Если да, то верно ли, что reinterpret_cast
даст указатель на полностью построенный объект? Потому как,
[Expr.reinterpret.cast/7],
[Expr.static.cast/13]
а также
[Basic.compound/4]
вместе, кажется, указывают, что это должно быть так.
По моим наблюдениям, реализация библиотек распределителя по умолчанию, похоже, похожа на это и без проблем! Действительно ли безопасно делать это?
Как мы можем быть уверены, что оба указателя будут одинаковыми, или мы можем?
Ответы
Ответ 1
Я удивлен, что никто не упомянул об этом дешевом контрпримеру:
struct foo {
static foo f;
// might seem dubious but doesn't violate anything
void* operator new(size_t, void* p) {return &f;}
};
Демо на Coliru.
Если вы не вызываете версию размещения для конкретного класса, ваше утверждение должно быть выполнено. Оба выражения должны представлять один и тот же адрес, как описано в другом ответе (главное, что стандартное невыделение operator new
просто возвращает аргумент указателя, а новое выражение не делает ничего интересного), и ни один из них не является указателем мимо конца какого-либо объекта, поэтому, по [expr.eq]/2, они сравниваются равными.
Ответ 2
18.6.1.3 Формы размещения [new.delete.placement]
void * operator new (std:: size_t size, void * ptr) noexcept;
Возвращает: ptr.
Недвусмысленно указано, что оператор размещения new
возвращает любой указатель, переданный ему. "Возвращает: ptr". Не могу получить более ясного, чем это.
Это, в значительной степени, запечатывает сделку для меня до тех пор, пока идет "возвращаемое значение из места размещения new": размещение new
ничего не делает с указателем, который он размещает, и всегда возвращает тот же указатель.
Все остальное в вашем вопросе относится к любым другим изменениям, которые могут возникнуть в результате других бросков; но вы спрашиваете конкретно о возвращаемом значении из места размещения new
, поэтому я полагаю, что вы принимаете, что все другие преобразования являются только преобразованиями типов и не влияют на фактический указатель, и вы только спрашивали о размещение нового - но также было бы возможно пройти другие роли и сделать аналогичное определение.