วัตถุประสงค์ของการ std :: launder คืออะไร?


242

P0137แนะนำเทมเพลตฟังก์ชั่น std::launderและทำการเปลี่ยนแปลงมากมายในมาตรฐานในส่วนที่เกี่ยวข้องกับสหภาพอายุการใช้งานและพอยน์เตอร์

ปัญหานี้แก้ไขได้อย่างไร การเปลี่ยนแปลงภาษาที่ฉันต้องระวังคืออะไร และสิ่งที่เราlaunderไอเอ็นจี?


2
คุณถามถึงกระดาษเองหรือstd::launderเปล่า? std::launderใช้เพื่อ "รับตัวชี้ไปยังวัตถุที่สร้างขึ้นในหน่วยเก็บข้อมูลที่ครอบครองโดยวัตถุที่มีอยู่ประเภทเดียวกันแม้ว่าจะมีสมาชิก const หรือสมาชิกอ้างอิง"
txtechhelp

7
ลิงค์ที่มีประโยชน์ในเรื่อง stackoverflow.com/questions/27003727/ …
Paul Rooney

สิ่งนี้ได้รับการเผยแพร่ใน VC2017 ในเวอร์ชัน 15.7.0
Damian

ตามมาตรฐานตัวชี้เป็นประเภทที่ไม่สำคัญดังนั้นการซักจะไม่ทำอะไรเลย ;)
curiousguy

คำตอบ:


250

std::launderเป็นชื่อที่เหมาะเจาะ แต่ก็ต่อเมื่อคุณรู้ว่ามันมีไว้เพื่ออะไร จะดำเนินการฟอกหน่วยความจำ

พิจารณาตัวอย่างในกระดาษ:

struct X { const int n; };
union U { X x; float f; };
...

U u = {{ 1 }};

ว่าคำสั่งดำเนินการเริ่มต้นรวมการเริ่มต้นครั้งแรกที่สมาชิกกับU{1}

เพราะnเป็นconstตัวแปรคอมไพเลอร์ที่เป็นอิสระที่จะคิดว่าu.x.nจะเสมอเป็น 1

ดังนั้นจะเกิดอะไรขึ้นถ้าเราทำสิ่งนี้:

X *p = new (&u.x) X {2};

เนื่องจากXเป็นเรื่องเล็กน้อยเราไม่จำเป็นต้องทำลายวัตถุเก่าก่อนที่จะสร้างวัตถุใหม่แทนที่มันดังนั้นจึงเป็นรหัสทางกฎหมายที่สมบูรณ์แบบ วัตถุใหม่จะมีnสมาชิกเป็น 2

บอกฉันหน่อยสิ ... จะเกิดu.x.nอะไรขึ้น?

คำตอบที่ชัดเจนจะ 2. แต่ที่ผิดเพราะคอมไพเลอร์ที่ได้รับอนุญาตที่จะคิดว่าอย่างแท้จริงconstตัวแปร (ไม่เพียงconst&แต่ตัวแปรวัตถุประกาศ const ) จะไม่เปลี่ยนแปลง แต่เราเพิ่งเปลี่ยนมัน

[basic.life] / 8คาถาสถานการณ์เมื่อมันตกลงเพื่อเข้าถึงวัตถุที่สร้างขึ้นใหม่ผ่านตัวแปร / พอยน์เตอร์ / อ้างอิงถึงวัตถุเก่า และการมีconstสมาชิกเป็นหนึ่งในปัจจัยที่ถูกตัดสิทธิ์

ดังนั้น ... เราจะพูดคุยu.x.nอย่างถูกต้องได้อย่างไร?

เราต้องฟอกความทรงจำของเรา:

assert(*std::launder(&u.x.n) == 2); //Will be true.

การฟอกเงินใช้เพื่อป้องกันไม่ให้คนติดตามว่าคุณได้รับเงินจากที่ไหน การฟอกหน่วยความจำถูกใช้เพื่อป้องกันคอมไพเลอร์ไม่ให้ติดตามตำแหน่งที่คุณได้รับวัตถุของคุณดังนั้นจึงบังคับให้หลีกเลี่ยงการปรับให้เหมาะสมที่อาจไม่มีผลบังคับใช้อีกต่อไป

ปัจจัยที่ทำให้ขาดคุณสมบัติอีกประการหนึ่งคือหากคุณเปลี่ยนประเภทของวัตถุ std::launderสามารถช่วยได้ที่นี่:

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

[basic.life] / 8บอกเราว่าหากคุณจัดสรรวัตถุใหม่ในที่จัดเก็บของเก่าคุณจะไม่สามารถเข้าถึงวัตถุใหม่ผ่านตัวชี้ไปยังวัตถุเก่า launderช่วยให้เราก้าวไปข้างหน้าได้


34
ดังนั้น tl ของฉันคือ dr ถูกต้อง: "การซักเป็นพื้นสำหรับประเภทที่ไม่ใช่ UB"
druckermanly

13
คุณช่วยอธิบายได้ไหมว่าทำไมเรื่องนี้ถึงเป็นจริง "เพราะnเป็นconstตัวแปรคอมไพเลอร์มีอิสระที่จะสมมติว่าu.x.nจะเป็น 1 เสมอ" มาตรฐานบอกว่าอยู่ที่ไหน ฉันถามเพราะปัญหาที่คุณชี้ให้เห็นดูเหมือนจะบอกเป็นนัยกับฉันว่ามันเป็นเรื่องผิดตั้งแต่แรก มันควรจะเป็นจริงภายใต้กฎ as-if ซึ่งล้มเหลวที่นี่ ฉันพลาดอะไรไป
user541686

10
@Mehrdad [basic.life] / 8: " ถ้า [... ] วัตถุใหม่ถูกสร้างขึ้นในที่เก็บสินค้าซึ่งวัตถุดั้งเดิมครอบครอง [... ] ชื่อของวัตถุต้นฉบับจะอ้างถึงวัตถุใหม่โดยอัตโนมัติ [... ] ถ้า: [... ] ประเภท [... ] ไม่มีสมาชิกข้อมูลที่ไม่คงที่ซึ่งมีคุณสมบัติแบบ const หรือผ่านการอ้างอิงประเภท [... ] "
ecatmur

14
@ Barry มาก; หากไม่มีวัตถุประเภท T อยู่ที่ที่อยู่ptrแสดงว่าคุณทำลายlaunderเงื่อนไขของดังนั้นจึงไม่มีประเด็นที่พูดถึงผลลัพธ์
TC

17
@NicolBolas One สามารถหวังว่า supercat จะทำหน้าที่ล็อบบี้คณะกรรมการมากเท่ากับที่พวกเขาต้องการคำตอบจากผู้ใช้ภาษาบุคคลที่สามใน SO นอกจากนี้ยังมีการเพิ่มประสิทธิภาพที่ดีคอมไพเลอร์จะเพิ่มประสิทธิภาพการแก้ปัญหาที่ถูกต้องที่คุณจะmemcpyเข้าสู่การตีความใหม่ในสถานที่ในการสนับสนุน (เช่นหละหลวมจัดตำแหน่ง) กระเช้าล่ะค่ะ
underscore_d
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.