Apakah UB untuk melanjutkan fungsi anggota coroutine dari objek yang masa hidupnya telah berakhir?

9

Pertanyaan ini berasal dari komentar ini: Penjelasan seumur hidup Lambda untuk C ++ 20 coroutine

tentang contoh ini:

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

Jadi pertanyaannya adalah apakah mengeksekusi coroutine yang dikembalikan oleh fooakan menghasilkan UB.

"Memanggil" fungsi anggota (setelah masa hidup objek berakhir) adalah UB: http://eel.is/c++draft/basic.life#6.2

... pointer apa pun yang mewakili alamat lokasi penyimpanan di mana objek akan atau terletak dapat digunakan tetapi hanya dengan cara yang terbatas. [...] Program memiliki perilaku yang tidak terdefinisi jika:

[...]

- pointer digunakan untuk mengakses anggota data non-statis atau memanggil fungsi anggota non-statis objek , atau

Namun, dalam contoh ini:

  • itu () operator lambda disebut sementara seumur hidup lambda masih berlaku
  • Kemudian ditangguhkan,
  • maka lambda dihancurkan,
  • dan kemudian fungsi anggota (operator ()) dilanjutkan di beberapa titik sesudahnya.

Apakah pembukaan kembali ini dianggap sebagai perilaku yang tidak terdefinisi?

Mike Lui
sumber
2
Mungkin jawaban berikut ini relevan stackoverflow.com/a/60495359/12345656 Kelihatannya sangat berbeda, tetapi juga tentang fungsi anggota saat eksekusi thispointer tidak valid. Pertimbangkan juga diskusi dalam komentar.
n314159

Jawaban:

2

[dcl.fct.def.coroutine] p3 :

The jenis janji dari coroutine adalah std::coroutine_traits<R, P1, ..., Pn>::promise_type, di mana Radalah jenis kembalinya fungsi, dan P1 ... Pnmerupakan urutan jenis parameter fungsi, didahului oleh jenis parameter objek implisit (12.4.1) jika coroutine adalah non-statis fungsi anggota.

Parameter objek implisit dalam contoh Anda adalah referensi const, dan karenanya referensi itu akan menggantung ketika eksekusi dilanjutkan setelah objek penutupan telah dihancurkan.

Namun, pada catatan objek yang dihancurkan selama pelaksanaan fungsi anggota, ini memang baik-baik saja, dan tidak lain dari standar itu sendiri menyiratkan ini dalam [dasar] :

Sebelum masa hidup suatu objek telah dimulai tetapi setelah penyimpanan yang akan ditempati objek telah dialokasikan atau, setelah masa hidup suatu objek telah berakhir dan sebelum penyimpanan yang digunakan oleh objek tersebut digunakan kembali atau dilepaskan, penunjuk apa pun yang mewakili alamat lokasi penyimpanan di mana objek akan atau terletak dapat digunakan tetapi hanya dengan cara yang terbatas. [...]

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

(NB: UB di atas adalah karena implisit thistidak dicuci dan masih mengacu pada parameter objek implisit.)

Jadi contoh Anda tampaknya terdefinisi dengan baik, tergantung pada gagasan bahwa dimulainya kembali eksekusi tidak jatuh di bawah aturan yang sama dengan doa asli. Perhatikan bahwa referensi ke objek penutup mungkin menjuntai, tetapi tidak diakses dengan cara apa pun antara suspensi dan dimulainya kembali.

Columbo
sumber
Apakah yang Anda maksud “pembukaan kembali dan penyelesaian” pada akhirnya?
Davis Herring
@ Davidviser Tidak, saya maksudkan secara khusus di dalam kerangka waktu "luar", di mana tidak jelas apakah referensi dapat ditugaskan ke referensi baru dll. Yang akan membutuhkan objek nyata. Fakta bahwa referensi tidak diakses secara tersembunyi adalah penting agar ini tidak menjadi UB
Columbo
Tapi itu tidak cukup untuk meninggalkan referensi yang menggantung sendirian sampai kembali; Anda harus meninggalkannya sendirian ( misalnya , dalam tubuh lambda) selamanya — selama sisa hidupnya, yang sampai selesai. Jadi mungkin itu harus "penskorsan dan penyelesaian".
Davis Herring
@ Davidviser Saya secara khusus menyebutkan interval itu, karena dalam contoh kita kita tahu yang lain aman.
Columbo
Tentu; Saya hanya menemukan kata-katanya membingungkan. Mungkin tidak ada orang lain yang melakukannya.
Davis Herring