std::launder
được đặt tên một cách khéo léo, mặc dù chỉ khi bạn biết nó dùng để làm gì. Nó thực hiện rửa bộ nhớ .
Xem xét ví dụ trong bài báo:
struct X { const int n; };
union U { X x; float f; };
...
U u = {{ 1 }};
Câu lệnh đó thực hiện khởi tạo tổng hợp, khởi tạo thành viên đầu tiên U
với {1}
.
Bởi vì n
là một const
biến, trình biên dịch có thể tự do cho rằng u.x.n
sẽ luôn là 1.
Vì vậy, những gì xảy ra nếu chúng ta làm điều này:
X *p = new (&u.x) X {2};
Vì X
là tầm thường, chúng ta không cần phải phá hủy đối tượng cũ trước khi tạo một đối tượng mới ở vị trí của nó, vì vậy đây là mã hoàn toàn hợp pháp. Đối tượng mới sẽ có n
thành viên là 2.
Vì vậy, hãy nói cho tôi ... những gì sẽ u.x.n
trở lại?
Câu trả lời rõ ràng sẽ là 2. Nhưng điều đó là sai, bởi vì trình biên dịch được phép giả định rằng một const
biến thực sự (không chỉ là một const&
, mà là một biến đối tượng được khai báo const
) sẽ không bao giờ thay đổi . Nhưng chúng tôi chỉ thay đổi nó.
[basic.life] / 8 giải thích các trường hợp khi truy cập vào đối tượng mới được tạo thông qua các biến / con trỏ / tham chiếu đến đối tượng cũ. Và có một const
thành viên là một trong những yếu tố không đủ tiêu chuẩn.
Vậy ... làm thế nào chúng ta có thể nói về u.x.n
đúng?
Chúng ta phải rửa trí nhớ của mình:
assert(*std::launder(&u.x.n) == 2); //Will be true.
Rửa tiền được sử dụng để ngăn chặn mọi người truy tìm nguồn tiền của bạn. Rửa bộ nhớ được sử dụng để ngăn trình biên dịch truy tìm nơi bạn lấy đối tượng của mình, do đó buộc nó phải tránh mọi tối ưu hóa có thể không còn áp dụng.
Một trong những yếu tố không đủ tiêu chuẩn là nếu bạn thay đổi loại đối tượng. std::launder
cũng có thể giúp đỡ ở đây:
aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));
[basic.life] / 8 cho chúng tôi biết rằng, nếu bạn phân bổ một đối tượng mới trong bộ lưu trữ của đối tượng cũ, bạn không thể truy cập đối tượng mới thông qua các con trỏ đến đối tượng cũ. launder
cho phép chúng tôi bước bên đó.
std::launder
?std::launder
được sử dụng để "lấy một con trỏ tới một đối tượng được tạo trong bộ lưu trữ bị chiếm bởi một đối tượng hiện có cùng loại, ngay cả khi nó có const hoặc thành viên tham chiếu."