Passando o ponteiro nulo para o novo posicionamento

O canal padrãonew operador é declarado em 18.6 [support.dynamic] ¶1 com uma especificação de exceção não lançada:

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

Esta função não faz nada, excetoreturn ptr; então é razoável que sejanoexceptEntretanto, de acordo com 5.3.4 [expr.new] ¶15 isto significa que o compilador deve verificar se ele não retorna null antes de invocar o construtor do objeto:

-15-
[Nota: a menos que uma função de alocação seja declarada com uma especificação de exceção não lançada (15.4), ela indica falha ao alocar armazenamento lançando umastd::bad_alloc exceção (Cláusula 15, 18.6.2.1); ele retorna um ponteiro não nulo de outra forma. Se a função de alocação for declarada com uma especificação de exceção não lançada, ela retornará null para indicar falha na alocação de armazenamento e um ponteiro não nulo, caso contrário.- end noteSe a função de alocação retorna null, a inicialização não deve ser feita, a função de desalocação não deve ser chamada e o valor da nova expressão deve ser nulo.

Parece-me que (especificamente para colocaçãonew, não em geral) esta verificação nula é um acerto de desempenho infeliz, embora pequeno.

Eu tenho depurado algum código onde o posicionamentonew estava sendo usado em um caminho de código muito sensível ao desempenho para melhorar a geração de código do compilador e a verificação de null foi observada na montagem. Ao fornecer um canal específico da turmanew Uma sobrecarga que é declarada com uma especificação de exceção de lançamento (mesmo que possivelmente não seja possível lançar) a ramificação condicional foi removida, o que também permitiu que o compilador gerasse código menor para as funções embutidas adjacentes. O resultado de dizer o posicionamentonew funçãopoderia jogar, mesmo quenão podia, foi um código melhor mensurável.

Então, estou pensando se a verificação nula é realmente necessária para a veiculaçãonew caso. A única maneira de retornar nulo é se você passar nulo. Embora seja possível, e aparentemente legal, escrever:

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

Não consigo ver por que isso seria útil, sugiro que seria melhor se o programador tivesse que verificar se há nulo explicitamente antes de usar o canalnew por exemplo.

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

Alguém já precisou de colocaçãonew manipular corretamente o caso de ponteiro nulo? (isto é, sem adicionar uma verificação explícitaptr é um local de memória válido.)

Eu estou querendo saber se seria razoável proibir a passagem de um ponteiro nulo para o posicionamento padrãonew função, e se não houver alguma maneira melhor para evitar o ramo desnecessário, além de tentar dizer ao compilador o valor não é nulo, e.

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

Ou:

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

N.B. Esta questão é intencionalmente marcada micro-otimização, eu sounão sugerindo que você ande por aí sobrecarregando o posicionamentonew para todos os seus tipos para "melhorar" o desempenho. Esse efeito foi observado em um caso crítico de desempenho muito específico e baseado em perfis e medições.

Atualizar: DR 1748 torna o comportamento indefinido usar um ponteiro nulo com posicionamento novo, portanto, os compiladores não precisam mais fazer a verificação.

questionAnswers(1)

yourAnswerToTheQuestion