È sicuro chiamare il posizionamento nuovo su `this` per un oggetto banale?


20

So che questa domanda è già stata posta più volte, ma non sono riuscito a trovare una risposta per questo caso particolare.

Diciamo che ho una classe banale che non possiede alcuna risorsa e ha distruttore vuoto e costruttore predefinito. Ha una manciata di variabili membro con inizializzazione in classe; nessuno di loro lo è const.

Voglio reinizializzare e obiettare tale classe senza scrivere il deInitmetodo a mano. È sicuro farlo in questo modo?

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

Non riesco a vedere alcun problema con esso - l'oggetto è sempre in stato valido, thispunta ancora allo stesso indirizzo; ma è C ++ quindi voglio esserne sicuro.


2
I membri dell'oggetto sono costanti?
NathanOliver,

2
Se questo è valido, sarebbe equivalente a *this = A{};?
Kevin,

2
@Amomum *this = A{};significa, this->operator=(A{});cioè creare un oggetto temporaneo e assegnarlo a *this, sostituendo i valori di tutti i membri dei dati con i valori del temporaneo. Dal momento che è quello che vuoi ed è (secondo me) più leggibile di un nuovo posizionamento, preferirei invece.
Kevin,

1
@Kevin oh, mio ​​male, hai ragione. Than - Penso che dovrebbe essere uguale se la copia è elisa?
Amomum,

1
invece di spiegare la classe a parole, è molto meglio scrivere la classe completa e non solo un metodo. vedi sscce.org
BЈовић,

Risposte:


17

Analogamente alla legalità di delete this, thisè consentito anche l' inserimento di nuovi utenti a quanto ne so. Inoltre, per quanto riguarda se thiso in seguito è possibile utilizzare altri puntatori / riferimenti preesistenti, esistono alcune restrizioni:

[Basic.life]

Se, al termine della durata di un oggetto e prima che la memoria di cui l'oggetto occupato sia riutilizzato o rilasciato, viene creato un nuovo oggetto nella posizione di memoria occupata dall'oggetto originale, un puntatore che puntava all'oggetto originale, un riferimento che riferito all'oggetto originale, oppure il nome dell'oggetto originale farà automaticamente riferimento al nuovo oggetto e, una volta iniziata la durata del nuovo oggetto, può essere utilizzato per manipolare il nuovo oggetto, se:

  • la memoria per il nuovo oggetto si sovrappone esattamente alla posizione di memoria occupata dall'oggetto originale e
  • il nuovo oggetto è dello stesso tipo dell'oggetto originale (ignorando i qualificatori cv di livello superiore) e
  • il tipo di oggetto originale non è const-qualificato e, se un tipo di classe, non contiene alcun membro di dati non statico il cui tipo è const-qualificato o un tipo di riferimento e
  • né l'oggetto originale né il nuovo oggetto sono un oggetto secondario potenzialmente sovrapposto ([intro.object]).

I primi due sono soddisfatti in questo esempio, ma gli ultimi due dovranno essere presi in considerazione.

Per quanto riguarda il terzo punto, dato che la funzione non è qualificata, dovrebbe essere abbastanza sicuro supporre che l'oggetto originale non sia const. L'errore è sul lato chiamante se la costanza è stata eliminata. Per quanto riguarda il membro const / reference, penso che possa essere verificato affermando che questo è assegnabile:

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

Naturalmente, poiché l'assegnabilità è un requisito, potresti invece semplicemente utilizzare il *this = {};quale mi aspetterei di produrre lo stesso programma. Un caso d'uso forse più interessante potrebbe essere il riutilizzo della memoria di *thisun oggetto di un altro tipo (che non soddisferebbe i requisiti per l'utilizzo this, almeno senza reinterpretazione + riciclaggio).

Analogamente delete this, il posizionamento nuovo thispotrebbe difficilmente essere descritto come "sicuro".


Interessante. È possibile assicurarsi che tutte queste condizioni siano soddisfatte con static_assert su alcuni tratti del tipo? Non sono sicuro che ce ne sia uno sui membri della const ...
Amomum,

1
@Amomum Non penso che essere oggetto secondario sia qualcosa che può essere testato. I membri const o di riferimento renderebbero la classe non assegnabile, cosa che non credo possa accadere altrimenti per una classe banale.
Eerorika,

significa che aliasing rigoroso entra in gioco per quanto riguarda la costanza? puoi fornire un esempio in cui ciò potrebbe entrare in gioco?
darune

1
@darune Crea un oggetto const, posizionalo come nuovo su di esso, usa il nome originale dell'oggetto (o un puntatore o riferimento preesistente) e il comportamento sarà indefinito. Praticamente lo stesso in effetti dell'ottimizzazione aliasing rigorosa, se non lo stesso del tutto.
Eerorika,

1
L'inverso di delete ptrè new T(). L'inverso di new(ptr)T{}è ptr->~T();. stackoverflow.com/a/8918942/845092
Mooing Duck il

7

Le regole che lo riguardano sono in [basic.life] / 5

Un programma può terminare la durata di qualsiasi oggetto riutilizzando la memoria occupata dall'oggetto o chiamando esplicitamente il distruttore per un oggetto di un tipo di classe. Per un oggetto di un tipo di classe, il programma non è tenuto a chiamare esplicitamente il distruttore prima che la memoria che occupa l'oggetto venga riutilizzata o rilasciata; tuttavia, se non esiste una chiamata esplicita al distruttore o se non viene utilizzata un'espressione di eliminazione per rilasciare la memoria, il distruttore non viene chiamato in modo implicito e qualsiasi programma che dipende dagli effetti collaterali prodotti dal distruttore ha un comportamento indefinito.

e [basic.life] / 8

Se, al termine della durata di un oggetto e prima che la memoria di cui l'oggetto occupato sia riutilizzato o rilasciato, viene creato un nuovo oggetto nella posizione di memoria occupata dall'oggetto originale, un puntatore che puntava all'oggetto originale, un riferimento che riferito all'oggetto originale, oppure il nome dell'oggetto originale farà automaticamente riferimento al nuovo oggetto e, una volta iniziata la durata del nuovo oggetto, può essere utilizzato per manipolare il nuovo oggetto, se:

  • la memoria per il nuovo oggetto si sovrappone esattamente alla posizione di memoria occupata dall'oggetto originale e

  • il nuovo oggetto è dello stesso tipo dell'oggetto originale (ignorando i qualificatori cv di livello superiore) e

  • il tipo di oggetto originale non è const-qualificato e, se un tipo di classe, non contiene alcun membro di dati non statico il cui tipo è const-qualificato o un tipo di riferimento e

  • né l'oggetto originale né il nuovo oggetto sono un oggetto secondario potenzialmente sovrapposto ([intro.object]).

Poiché il tuo oggetto è banale, non devi preoccuparti di [basic.life] / 5 e fintanto che soddisfi i punti elenco di [basic.life] / 8, allora è sicuro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.