Pytasz o sam papier czy o std::launder? std::laundersłuży do „uzyskania wskaźnika do obiektu utworzonego w pamięci zajmowanej przez istniejący obiekt tego samego typu, nawet jeśli ma on stałe lub referencyjne elementy”.
Według standardu wskaźniki są trywialne, więc pranie nic nie robi. ;)
ciekawy
Odpowiedzi:
250
std::launderjest trafnie nazwany, ale tylko jeśli wiesz po co. Wykonuje pranie pamięci .
Rozważ przykład w artykule:
struct X {constint n;};union U { X x;float f;};...
U u ={{1}};
Ta instrukcja wykonuje agregację inicjując, inicjując pierwszego członka Uz{1} .
Ponieważ njest to constzmienna, kompilator może swobodnie przyjąć, że u.x.npowinien zawsze być 1.
Co się stanie, jeśli to zrobimy:
X *p =new(&u.x) X {2};
Ponieważ Xjest to banalne, nie musimy niszczyć starego obiektu przed utworzeniem nowego na jego miejscu, więc jest to całkowicie legalny kod. Nowy obiekt będzie miał nelement członkowski 2.
Więc powiedz mi ... co u.x.npowróci?
Oczywistą odpowiedzią będzie 2. Ale to źle, ponieważ kompilator może założyć, że prawdziwie constzmienna (nie tylko deklarowanaconst& zmienna obiektowa ) nigdy się nie zmieni . Ale właśnie to zmieniliśmy. const
[basic.life] / 8 określa okoliczności, w których można uzyskać dostęp do nowo utworzonego obiektu za pomocą zmiennych / wskaźników / odniesień do starego. Posiadanie constczłonka jest jednym z czynników dyskwalifikujących.
Więc ... jak możemy u.x.nwłaściwie rozmawiać ?
Musimy prać naszą pamięć:
assert(*std::launder(&u.x.n)==2);//Will be true.
Pranie pieniędzy ma na celu zapobieganie śledzeniu przez klientów miejsca, z którego otrzymałeś pieniądze. Pranie pamięci jest używane, aby uniemożliwić kompilatorowi śledzenie, skąd wziął się twój obiekt, w ten sposób zmuszając go do uniknięcia optymalizacji, które mogą już nie mieć zastosowania.
Innym z czynników dyskwalifikujących jest zmiana typu obiektu. std::laundermoże tu również pomóc:
[basic.life] / 8 mówi nam, że jeśli przydzielisz nowy obiekt do pamięci starego, nie będziesz mógł uzyskać dostępu do nowego obiektu poprzez wskaźniki do starego. launderpozwala nam to zrobić krok po kroku.
Więc czy mój tl; dr ma rację: „pranie jest w zasadzie dla punningowania innego niż UB”?
druckermanly
13
Czy możesz wyjaśnić, dlaczego to prawda? „Ponieważ njest to constzmienna, kompilator może założyć, że u.x.nzawsze będzie to 1.” Gdzie w standardzie to mówi? Pytam, ponieważ sam problem, który wskazałeś, wydaje mi się sugerować, że jest on w pierwszej kolejności fałszywy. Powinno to być prawdą tylko zgodnie z zasadą „jak gdyby”, która tutaj zawodzi. czego mi brakuje?
user541686,
10
@Mehrdad [basic.life] / 8: " Jeśli [...] zostanie utworzony nowy obiekt w miejscu przechowywania, które zajmował [...] oryginalny obiekt, nazwa oryginalnego obiektu automatycznie odniesie się do nowego obiektu [...] jeżeli: [...] typ [...] nie zawiera żadnego elementu niestatycznego, którego typ jest stały lub typu odniesienia [...] "
ecatmur
14
@Barry Very; jeśli pod adresem nie ma obiektów typu T ptr, oznacza to, że nie spełniasz launderwarunku, więc nie ma sensu mówić o wyniku.
TC
17
@NicolBolas Można mieć tylko nadzieję, że supercat będzie lobbował w komitetach w takim samym stopniu, jak w nieskończoność domaga się odpowiedzi od innych użytkowników języka na platformie SO. Poza tym, dobry optymalizacji kompilator zoptymalizować poprawne rozwiązanie memcpydo o reinterpretacji w miejscu na obsługiwane (tzn lax wyrównanie) platform anyway .
std::launder
?std::launder
służy do „uzyskania wskaźnika do obiektu utworzonego w pamięci zajmowanej przez istniejący obiekt tego samego typu, nawet jeśli ma on stałe lub referencyjne elementy”.Odpowiedzi:
std::launder
jest trafnie nazwany, ale tylko jeśli wiesz po co. Wykonuje pranie pamięci .Rozważ przykład w artykule:
Ta instrukcja wykonuje agregację inicjując, inicjując pierwszego członka
U
z{1}
.Ponieważ
n
jest toconst
zmienna, kompilator może swobodnie przyjąć, żeu.x.n
powinien zawsze być 1.Co się stanie, jeśli to zrobimy:
Ponieważ
X
jest to banalne, nie musimy niszczyć starego obiektu przed utworzeniem nowego na jego miejscu, więc jest to całkowicie legalny kod. Nowy obiekt będzie miałn
element członkowski 2.Więc powiedz mi ... co
u.x.n
powróci?Oczywistą odpowiedzią będzie 2. Ale to źle, ponieważ kompilator może założyć, że prawdziwie
const
zmienna (nie tylko deklarowanaconst&
zmienna obiektowa ) nigdy się nie zmieni . Ale właśnie to zmieniliśmy.const
[basic.life] / 8 określa okoliczności, w których można uzyskać dostęp do nowo utworzonego obiektu za pomocą zmiennych / wskaźników / odniesień do starego. Posiadanie
const
członka jest jednym z czynników dyskwalifikujących.Więc ... jak możemy
u.x.n
właściwie rozmawiać ?Musimy prać naszą pamięć:
Pranie pieniędzy ma na celu zapobieganie śledzeniu przez klientów miejsca, z którego otrzymałeś pieniądze. Pranie pamięci jest używane, aby uniemożliwić kompilatorowi śledzenie, skąd wziął się twój obiekt, w ten sposób zmuszając go do uniknięcia optymalizacji, które mogą już nie mieć zastosowania.
Innym z czynników dyskwalifikujących jest zmiana typu obiektu.
std::launder
może tu również pomóc:[basic.life] / 8 mówi nam, że jeśli przydzielisz nowy obiekt do pamięci starego, nie będziesz mógł uzyskać dostępu do nowego obiektu poprzez wskaźniki do starego.
launder
pozwala nam to zrobić krok po kroku.źródło
n
jest toconst
zmienna, kompilator może założyć, żeu.x.n
zawsze będzie to 1.” Gdzie w standardzie to mówi? Pytam, ponieważ sam problem, który wskazałeś, wydaje mi się sugerować, że jest on w pierwszej kolejności fałszywy. Powinno to być prawdą tylko zgodnie z zasadą „jak gdyby”, która tutaj zawodzi. czego mi brakuje?ptr
, oznacza to, że nie spełniaszlaunder
warunku, więc nie ma sensu mówić o wyniku.memcpy
do o reinterpretacji w miejscu na obsługiwane (tzn lax wyrównanie) platform anyway .