P0137 wprowadza szablon funkcji std::launder
i 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
?
P0137 wprowadza szablon funkcji std::launder
i 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
?
Odpowiedzi:
std::launder
jest 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 U
z{1}
.
Ponieważ n
jest to const
zmienna, kompilator może swobodnie przyjąć, że u.x.n
powinien zawsze być 1.
Co się stanie, jeśli to zrobimy:
X *p = new (&u.x) X {2};
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ęć:
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::launder
moż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. launder
pozwala nam to zrobić krok po kroku.
n
jest to const
zmienna, kompilator może założyć, że u.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łniasz launder
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 .
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”.