Передача нулевого указателя на размещение нового
Размещение по умолчаниюnew
Оператор объявлен в 18.6 [support.dynamic] ¶1 с ненулевой спецификацией исключения:
void* operator new (std::size_t size, void* ptr) noexcept;
Эта функция не делает ничего, кромеreturn ptr;
поэтому разумноnoexcept
однако в соответствии с 5.3.4 [expr.new] this15 это означает, что компилятор должен проверить, что он не возвращает ноль перед вызовом конструктора объекта:
-15-
[Примечание: если функция выделения не объявлена со спецификацией исключения без отбрасывания (15.4), это указывает на сбой в распределении памяти путемstd::bad_alloc
исключение (пункт 15, 18.6.2.1); в противном случае он возвращает ненулевой указатель. Если функция выделения объявлена со спецификацией исключения без отбрасывания, она возвращает ноль, чтобы указать на неудачу в выделении памяти, и ненулевой указатель в противном случае.—Конечная записка] Если функция выделения возвращает ноль, инициализация не должна выполняться, функция освобождения не должна вызываться, а значение нового выражения должно быть нулевым.
Мне кажется, что (специально для размещенияnew
, не в общем) эта нулевая проверка - неудачный удар по производительности, хотя и небольшой.
Я отлаживал код где размещениеnew
использовался в очень чувствительном к производительности пути кода, чтобы улучшить генерацию кода компилятора, и проверка на ноль наблюдалась в сборке. Предоставляя размещение для конкретного классаnew
Перегрузка, которая объявляется со спецификацией выброса исключения (даже если она не может выбрасывать), условная ветвь была удалена, что также позволило компилятору генерировать меньший код для окружающих встроенных функций. Результат произнесения размещенияnew
функциямог бросить, хотя этоне можетбыл заметно лучше кода.
Поэтому мне было интересно, действительно ли для размещения требуется нулевая проверкаnew
кейс. Единственный способ вернуть NULL - это передать NULL. Хотя возможно и, по-видимому, законно, написать:
void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );
Я не могу понять, почему это было бы полезно, я полагаю, что было бы лучше, если бы программисту пришлось явно проверять нулевое значение перед использованием размещенияnew
например
Obj* obj = ptr ? new (ptr) Obj() : nullptr;
Кто-нибудь когда-либо нуждался в размещенииnew
правильно обрабатывать случай нулевого указателя? (т.е. без добавления явной проверки, чтоptr
является допустимым местом в памяти.)
Мне интересно, было бы разумно запретить передачу нулевого указателя на размещение по умолчаниюnew
функции, и если нет, то есть ли лучший способ избежать ненужной ветви, кроме попытки сообщить компилятору, что значение не равно нулю, например,
void* ptr = getAddress();
(void) *(Obj*)ptr; // inform the optimiser that dereferencing pointer is valid
Obj* obj = new (ptr) Obj();
Или же:
void* ptr = getAddress();
if (!ptr)
__builtin_unreachable(); // same, but not portable
Obj* obj = new (ptr) Obj();
Нотабене Этот вопрос намеренно помечен микро-оптимизации, яне предполагая, что вы обойдете место перегрузкиnew
для всех ваших типов, чтобы «улучшить» производительность. Этот эффект был замечен в очень специфическом критическом случае производительности и основан на профилировании и измерении.
Обновить: DR 1748 делает неопределенным поведение использовать нулевой указатель с новым размещением, поэтому компиляторам больше не требуется выполнять проверку.