std::launder
ha un nome appropriato, anche se solo se sai a cosa serve. Esegue il riciclaggio di memoria .
Considera l'esempio nel documento:
struct X { const int n; };
union U { X x; float f; };
...
U u = {{ 1 }};
Tale istruzione esegue l'inizializzazione aggregata, inizializzando il primo membro di U
con {1}
.
Perché n
è una const
variabile, il compilatore è libero di assumere che u.x.n
deve sempre essere 1.
Quindi cosa succede se lo facciamo:
X *p = new (&u.x) X {2};
Perché X
è banale, non è necessario distruggere il vecchio oggetto prima di crearne uno nuovo al suo posto, quindi questo è un codice perfettamente legale. Il nuovo oggetto avrà il suo n
membro essere 2.
Allora dimmi ... cosa u.x.n
tornerà?
La risposta ovvia sarà 2. Ma questo è sbagliato, perché al compilatore è consentito supporre che una const
variabile veramente (non solo una const&
, ma una variabile oggetto dichiarata const
) non cambierà mai . Ma l'abbiamo appena cambiato.
[basic.life] / 8 spiega le circostanze in cui è OK accedere all'oggetto appena creato attraverso variabili / puntatori / riferimenti a quello vecchio. E avere un const
membro è uno dei fattori squalificanti.
Quindi ... come possiamo parlare u.x.n
correttamente?
Dobbiamo riciclare la nostra memoria:
assert(*std::launder(&u.x.n) == 2); //Will be true.
Il riciclaggio di denaro viene utilizzato per impedire alle persone di rintracciare da dove hai preso i tuoi soldi. Il riciclaggio di memoria viene utilizzato per impedire al compilatore di tracciare da dove proviene l'oggetto, costringendolo in tal modo a evitare eventuali ottimizzazioni che potrebbero non essere più applicabili.
Un altro dei fattori squalificanti è se si modifica il tipo di oggetto. std::launder
può aiutare anche qui:
aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));
[basic.life] / 8 ci dice che, se si assegna un nuovo oggetto nella memoria di quello vecchio, non è possibile accedere al nuovo oggetto tramite i puntatori al vecchio. launder
ci consente di fare questo.
std::launder
?std::launder
viene utilizzato per "ottenere un puntatore a un oggetto creato nella memoria occupata da un oggetto esistente dello stesso tipo, anche se ha membri const o di riferimento".