Возвращаемое значение размещения 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, поэтому я полагаю, что вы принимаете, что все другие преобразования являются только преобразованиями типов и не влияют на фактический указатель, и вы только спрашивали о размещение нового - но также было бы возможно пройти другие роли и сделать аналогичное определение.