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 sejanoexcept
Entretanto, 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.