P0137แนะนำเทมเพลตฟังก์ชั่น std::launder
และทำการเปลี่ยนแปลงมากมายในมาตรฐานในส่วนที่เกี่ยวข้องกับสหภาพอายุการใช้งานและพอยน์เตอร์
ปัญหานี้แก้ไขได้อย่างไร การเปลี่ยนแปลงภาษาที่ฉันต้องระวังคืออะไร และสิ่งที่เราlaunder
ไอเอ็นจี?
P0137แนะนำเทมเพลตฟังก์ชั่น std::launder
และทำการเปลี่ยนแปลงมากมายในมาตรฐานในส่วนที่เกี่ยวข้องกับสหภาพอายุการใช้งานและพอยน์เตอร์
ปัญหานี้แก้ไขได้อย่างไร การเปลี่ยนแปลงภาษาที่ฉันต้องระวังคืออะไร และสิ่งที่เราlaunder
ไอเอ็นจี?
คำตอบ:
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
ช่วยให้เราก้าวไปข้างหน้าได้
n
เป็นconst
ตัวแปรคอมไพเลอร์มีอิสระที่จะสมมติว่าu.x.n
จะเป็น 1 เสมอ" มาตรฐานบอกว่าอยู่ที่ไหน ฉันถามเพราะปัญหาที่คุณชี้ให้เห็นดูเหมือนจะบอกเป็นนัยกับฉันว่ามันเป็นเรื่องผิดตั้งแต่แรก มันควรจะเป็นจริงภายใต้กฎ as-if ซึ่งล้มเหลวที่นี่ ฉันพลาดอะไรไป
ptr
แสดงว่าคุณทำลายlaunder
เงื่อนไขของดังนั้นจึงไม่มีประเด็นที่พูดถึงผลลัพธ์
memcpy
เข้าสู่การตีความใหม่ในสถานที่ในการสนับสนุน (เช่นหละหลวมจัดตำแหน่ง) กระเช้าล่ะค่ะ
std::launder
เปล่า?std::launder
ใช้เพื่อ "รับตัวชี้ไปยังวัตถุที่สร้างขึ้นในหน่วยเก็บข้อมูลที่ครอบครองโดยวัตถุที่มีอยู่ประเภทเดียวกันแม้ว่าจะมีสมาชิก const หรือสมาชิกอ้างอิง"