Jaki jest cel std :: prania?

242

P0137 wprowadza szablon funkcji std::launderi wprowadza wiele, wiele zmian w standardzie w sekcjach dotyczących związków, czasu życia i wskaźników.

Jaki problem rozwiązuje ten papier? Jakie zmiany w języku muszę znać? A co my launder?

Barry
źródło
2
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”.
txtechhelp
7
przydatny link na ten temat. Także to pytanie stackoverflow.com/questions/27003727/...
Paul Rooney
Zostało to wydane w VC2017 w wersji 15.7.0
Damian
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 { const int 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:

aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));

[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.

Nicol Bolas
źródło
34
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 .
underscore_d