Pasando el puntero nulo a la colocación nueva.

La colocación por defectonew el operador se declara en 18.6 [support.dynamic] ¶1 con una especificación de excepción no lanzable:

void* operator new (std::size_t size, void* ptr) noexcept;

Esta función no hace nada exceptoreturn ptr; por lo que es razonable que seanoexceptSin embargo, de acuerdo con 5.3.4 [expr.new] ¶15, esto significa que el compilador debe verificar que no devuelva nulo antes de invocar el constructor del objeto:

-15-
[Nota: a menos que una función de asignación se declare con una especificación de excepción de no lanzamiento (15.4), indica que no se puede asignar almacenamiento lanzando unstd::bad_alloc excepción (Cláusula 15, 18.6.2.1); de lo contrario, devuelve un puntero no nulo. Si la función de asignación se declara con una especificación de excepción no lanzable, devuelve nulo para indicar que no se puede asignar el almacenamiento y, de lo contrario, un puntero no nulo.Nota final] Si la función de asignación devuelve nula, no se realizará la inicialización, no se llamará a la función de desasignación y el valor de la nueva expresión será nulo.

Me parece que (específicamente para la colocaciónnew, no en general) esta comprobación nula es un golpe de rendimiento desafortunado, aunque pequeño.

He estado depurando algún código donde la colocaciónnew se estaba utilizando en una ruta de código muy sensible al rendimiento para mejorar la generación de código del compilador y se observó la comprobación de nulo en el ensamblaje. Al proporcionar una colocación específica de clasenew la sobrecarga declarada con una especificación de excepción de lanzamiento (aunque no puede lanzar) se eliminó la rama condicional, lo que también permitió que el compilador generara un código más pequeño para las funciones en línea circundantes. El resultado de decir la colocación.new funciónpodría lanzar, aunque seano podría, fue considerablemente mejor el código.

Así que me he estado preguntando si el cheque nulo es realmente necesario para la colocaciónnew caso. La única forma en que puede devolverse nulo es si lo pasas nulo. Aunque es posible, y aparentemente legal, escribir:

void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );

No puedo ver por qué sería útil, sugiero que sería mejor si el programador tuviera que verificar nulo explícitamente antes de usar la ubicaciónnew p.ej.

Obj* obj = ptr ? new (ptr) Obj() : nullptr;

¿Alguna vez alguien ha necesitado colocación?new Cómo manejar correctamente el caso de puntero nulo? (es decir, sin agregar un control explícito de queptr es una ubicación de memoria válida.)

Me pregunto si sería razonable prohibir pasar un puntero nulo a la ubicación predeterminadanew función, y si no es así, si hay alguna forma mejor de evitar la rama innecesaria, aparte de intentar decirle al compilador que el valor no es nulo, por ejemplo.

void* ptr = getAddress();
(void) *(Obj*)ptr;   // inform the optimiser that dereferencing pointer is valid
Obj* obj = new (ptr) Obj();

O:

void* ptr = getAddress();
if (!ptr)
  __builtin_unreachable();  // same, but not portable
Obj* obj = new (ptr) Obj();

nótese bien Esta pregunta está etiquetada intencionalmente micro-optimización, estoyno sugiriendo que vayas alrededor de la colocación de sobrecarganew para todos sus tipos para "mejorar" el rendimiento. Este efecto se notó en un caso muy específico de rendimiento crítico y basado en la generación de perfiles y la medición.

Actualizar: DR 1748 hace que sea un comportamiento indefinido utilizar un puntero nulo con una ubicación nueva, por lo que los compiladores ya no están obligados a realizar la comprobación.

Respuestas a la pregunta(1)

Su respuesta a la pregunta