มันเป็น UB ที่จะกลับมาทำงานเป็นสมาชิก coroutine ของวัตถุที่อายุการใช้งานสิ้นสุดลงหรือไม่?


9

คำถามนี้เกิดจากความคิดเห็นนี้: คำอธิบายตลอดอายุการใช้งานแลมบ์ดาสำหรับ coroutines C ++ 20

เกี่ยวกับตัวอย่างนี้:

auto foo() -> folly::coro::Task<int> {
    auto task = []() -> folly::coro::Task<int> {
        co_return 1;
    }();
    return task;
}

ดังนั้นคำถามคือการดำเนินการ coroutine กลับโดยfooจะส่งผลใน UB

"การเรียก" ฟังก์ชันสมาชิก (หลังจากสิ้นสุดอายุการใช้งานของวัตถุ) คือ UB: http://eel.is/c++draft/basic.life#6.2

... ตัวชี้ใด ๆ ที่แสดงที่อยู่ของที่เก็บสินค้าซึ่งวัตถุนั้นจะเป็นหรือที่ตั้งอาจถูกนำมาใช้ แต่ในรูปแบบที่ จำกัด เท่านั้น [... ] โปรแกรมมีพฤติกรรมที่ไม่ได้กำหนดหาก:

[ ... ]

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

อย่างไรก็ตามในตัวอย่างนี้:

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

การเริ่มต้นใหม่นี้ถือเป็นพฤติกรรมที่ไม่ได้กำหนดหรือไม่?


2
บางทีคำตอบต่อไปนี้อาจเกี่ยวข้องกับstackoverflow.com/a/60495359/12345656ดูเหมือนว่าจะแตกต่างกันมาก แต่ก็เป็นเรื่องเกี่ยวกับฟังก์ชั่นสมาชิกระหว่างที่การดำเนินการthisตัวชี้ไม่ถูกต้อง พิจารณาการอภิปรายในความคิดเห็นด้วย
n314159

คำตอบ:


2

[dcl.fct.def.coroutine] p3 :

ประเภทสัญญาของ coroutine เป็นstd::coroutine_traits<R, P1, ..., Pn>::promise_typeที่Rเป็นชนิดที่การกลับมาของฟังก์ชั่นและ P1 ... Pnมีลำดับของประเภทของพารามิเตอร์ฟังก์ชั่นที่ นำโดยประเภทของพารามิเตอร์วัตถุนัย (12.4.1) ถ้า coroutine เป็นไม่คงที่ ฟังก์ชั่นสมาชิก

พารามิเตอร์วัตถุ implicit อยู่ในตัวอย่างของคุณการอ้างอิง const และด้วยเหตุนี้การอ้างอิงนั้นจะห้อยเมื่อดำเนินการต่อหลังจากวัตถุปิดถูกทำลาย

อย่างไรก็ตามในบันทึกของวัตถุที่ถูกทำลายในระหว่างการดำเนินการของฟังก์ชั่นสมาชิกสิ่งนี้ย่อมดีต่อ se และไม่มีสิ่งอื่นใดนอกเหนือจากมาตรฐานที่บ่งบอกถึงสิ่งนี้ใน[พื้นฐาน] :

ก่อนที่อายุการใช้งานของวัตถุจะเริ่มต้น แต่หลังจากการจัดเก็บข้อมูลที่วัตถุจะครอบครองได้รับการจัดสรรหรือหลังจากอายุการใช้งานของวัตถุได้สิ้นสุดลงและก่อนที่การจัดเก็บข้อมูลที่วัตถุครอบครองถูกนำมาใช้ซ้ำหรือปล่อยตัวชี้ใด ๆ ตำแหน่งที่เก็บข้อมูลที่วัตถุนั้นจะอยู่หรืออาจถูกนำมาใช้ แต่มีวิธี จำกัด เท่านั้น [ ... ]

void B::mutate() {
  new (this) D2;    // reuses storage --- ends the lifetime of *this
  f();              // undefined behavior
  ... = this;       // OK, this points to valid memory
}

(หมายเหตุ: UB ข้างต้นเป็นเพราะนัยthisไม่ได้ฟอกและยังหมายถึงพารามิเตอร์วัตถุโดยปริยาย)

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


คุณหมายถึง“ การเริ่มต้นใหม่และการเสร็จสิ้น” ในตอนท้ายหรือไม่
Davis Herring

@DavisHerring ไม่ฉันหมายถึงเฉพาะภายในกรอบเวลา "นอก" ซึ่งไม่ชัดเจนว่าการอ้างอิงสามารถกำหนดให้กับการอ้างอิงใหม่ ฯลฯ ซึ่งจะต้องใช้วัตถุจริงหรือไม่ ความจริงที่ว่าการอ้างอิงไม่สามารถเข้าถึงได้ในลักษณะที่ซ่อนเร้นเป็นสิ่งสำคัญสำหรับสิ่งนี้ไม่ให้เป็น UB
Columbo

แต่มันก็ไม่เพียงพอที่จะปล่อยให้การอ้างอิงห้อยอยู่คนเดียวจนกระทั่งเริ่มต้นใหม่; คุณต้องทิ้งมันไว้ตามลำพัง ( เช่นในร่างกายแลมบ์ดา) ตลอดไป - ตลอดช่วงเวลาที่เหลือของชีวิตซึ่งจะสิ้นสุดลง ดังนั้นบางทีมันควรจะเป็น "การระงับและการทำให้สมบูรณ์"
Davis Herring

@DavisHerring ฉันพูดถึงช่วงเวลานั้นโดยเฉพาะเพราะในตัวอย่างของเราเรารู้ว่าอีกช่วงหนึ่งจะปลอดภัย
Columbo

แน่นอนว่า; ฉันแค่พบว่าถ้อยคำสับสน อาจไม่มีใครทำ
Davis Herring
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.