É seguro chamar o posicionamento novo em `this` para objeto trivial?

20

Sei que essa pergunta já foi feita várias vezes, mas não consegui encontrar uma resposta para esse caso em particular.

Digamos que eu tenho uma classe trivial que não possui nenhum recurso e tem destruidor vazio e construtor padrão. Possui várias variáveis ​​de membro com inicialização em classe; nenhum deles é const.

Quero reinicializar e objetar essa classe sem escrever o deInitmétodo manualmente. É seguro fazer assim?

void A::deInit()
{
  new (this)A{};
}

Não vejo nenhum problema com ele - o objeto está sempre em estado válido, thisainda aponta para o mesmo endereço; mas é C ++, então eu quero ter certeza.

Amomum
fonte
2
Existem membros do objeto const?
NathanOliver 29/10/19
2
Se isso for válido, seria equivalente a *this = A{};?
21419 Kevin
2
@Amomum *this = A{};significa, this->operator=(A{});ou seja, criar um objeto temporário e atribuí-lo a *this, substituindo os valores de todos os membros de dados pelos valores do temporário. Como é isso que você deseja e é (na minha opinião) mais legível do que uma nova veiculação, eu iria com isso.
21419 Kevin
11
@ Kevin oh, meu mal, você está certo. Than - Eu acho que deveria ser igual se cópia é elided?
Amomum
11
em vez de explicar a classe em palavras, é muito melhor escrever classe completa, e não apenas um método. Veja sscce.org
Última

Respostas:

17

Da mesma forma que a legalidade de delete this, a colocação de novos em thistambém é permitida até onde eu sei. Além disso, sobre se thisou outros ponteiros / referências pré-existentes podem ser usados ​​posteriormente, existem algumas restrições:

[vida básica]

Se, após o término da vida útil de um objeto e antes da reutilização ou liberação do armazenamento ocupado pelo objeto, um novo objeto será criado no local de armazenamento ocupado pelo objeto original, um ponteiro que apontasse para o objeto original, uma referência que referente ao objeto original, ou o nome do objeto original fará referência automaticamente ao novo objeto e, uma vez iniciada a vida útil do novo objeto, pode ser usada para manipular o novo objeto, se:

  • o armazenamento para o novo objeto se sobrepõe exatamente ao local de armazenamento que o objeto original ocupava, e
  • o novo objeto é do mesmo tipo que o objeto original (ignorando os qualificadores de cv de nível superior) e
  • o tipo do objeto original não é qualificado pela const e, se um tipo de classe, não contém nenhum membro de dados não estáticos cujo tipo é qualificado pela const ou um tipo de referência, e
  • nem o objeto original nem o novo objeto são um subobjeto potencialmente sobreposto ([intro.object]).

Os dois primeiros são satisfeitos neste exemplo, mas os dois últimos precisarão ser levados em consideração.

Em relação ao terceiro ponto, considerando que a função não é qualificada por const, deve ser bastante seguro assumir que o objeto original não é const. A falha está no lado do chamador se a constância tiver sido descartada. Em relação ao membro const / reference, acho que pode ser verificado afirmando que isso é atribuível:

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

Obviamente, como a atribuição é um requisito, você poderia simplesmente usar o *this = {};que eu esperaria produzir o mesmo programa. Um caso de uso talvez mais interessante pode ser reutilizar a memória de *thisum objeto de outro tipo (o que falharia nos requisitos de uso this, pelo menos sem reinterpretar + lavagem).

Semelhante a delete this, o posicionamento novo no thisdificilmente poderia ser descrito como "seguro".

eerorika
fonte
Interessante. É possível garantir que todas essas condições sejam satisfeitas com static_assert em alguns traços de tipo? Não tenho certeza se há uma sobre os membros const ...
Amomum
11
@ Amomum Eu não acho que ser subobjeto é algo que pode ser testado. Os membros const ou de referência tornariam a classe não atribuível, o que não acho que possa acontecer de outra forma em uma classe trivial.
eerorika
isso significa que o alias estrito entra em jogo com relação à constância? você pode fornecer um exemplo em que isso pode entrar em jogo?
darune 29/10/19
11
@darune Crie um objeto const, com uma nova colocação nele, use o nome original do objeto (ou um ponteiro ou referência pré-existente) e o comportamento será indefinido. Praticamente o mesmo efeito que a otimização estrita de alias, se não o mesmo.
eerorika
11
O inverso de delete ptré new T(). O inverso de new(ptr)T{}é ptr->~T();. stackoverflow.com/a/8918942/845092
Mooing Duck
7

As regras que cobrem isso estão em [basic.life] / 5

Um programa pode terminar a vida útil de qualquer objeto reutilizando o armazenamento que o objeto ocupa ou chamando explicitamente o destruidor para um objeto de um tipo de classe. Para um objeto de um tipo de classe, não é necessário que o programa chame o destruidor explicitamente antes que o armazenamento que o objeto ocupa seja reutilizado ou liberado; no entanto, se não houver uma chamada explícita ao destruidor ou se uma expressão de exclusão não for usada para liberar o armazenamento, o destruidor não será chamado implicitamente e qualquer programa que dependa dos efeitos colaterais produzidos pelo destruidor terá um comportamento indefinido.

e [vida básica] / 8

Se, após o término da vida útil de um objeto e antes da reutilização ou liberação do armazenamento ocupado pelo objeto, um novo objeto será criado no local de armazenamento ocupado pelo objeto original, um ponteiro que apontasse para o objeto original, uma referência que referente ao objeto original, ou o nome do objeto original fará referência automaticamente ao novo objeto e, uma vez iniciada a vida útil do novo objeto, pode ser usada para manipular o novo objeto, se:

  • o armazenamento para o novo objeto se sobrepõe exatamente ao local de armazenamento que o objeto original ocupava, e

  • o novo objeto é do mesmo tipo que o objeto original (ignorando os qualificadores de cv de nível superior) e

  • o tipo do objeto original não é qualificado pela const e, se um tipo de classe, não contém nenhum membro de dados não estáticos cujo tipo é qualificado pela const ou um tipo de referência, e

  • nem o objeto original nem o novo objeto são um subobjeto potencialmente sobreposto ([intro.object]).

Como seu objeto é trivial, você não precisa se preocupar com [basic.life] / 5 e desde que atenda aos pontos de referência de [basic.life] / 8, então é seguro.

NathanOliver
fonte