Чи варто UB відновити функцію члена члена об'єкта, термін експлуатації якого закінчився?


9

Це питання випливає з цього коментаря: пояснення життя Ламбди для C ++ 20 спільних процедур

щодо цього прикладу:

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

Таким чином, питання полягає в тому, чи 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 :

Тип обіцянки підпрограми є std::coroutine_traits<R, P1, ..., Pn>::promise_type, де Rє типом повернення функції, і P1 ... Pnє послідовністю типів параметрів функції, передуючим типу імпліцитного параметра об'єкта (12.4.1), якщо підпрограма є нестатичною членська функція.

Параметр неявного об'єкта у вашому прикладі є посиланням const, а значить, посилання буде звисати, коли виконання буде відновлено після знищення об'єкта закриття.

Однак, на увазі об'єктів, що руйнуються під час виконання функції-члена, це справді нормально, і не що інше, ніж сам стандарт передбачає це в [basic] :

До початку життя об’єкта, але після того, як було виділено сховище, яке буде займати об'єкт, або після закінчення терміну експлуатації об'єкта і до того, як зберігання, яке займав об'єкт, буде повторно використане або звільнено, будь-який покажчик, що представляє адресу місце зберігання, де об’єкт буде або знаходився, може використовуватися, але лише обмеженими способами. [...]

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не відмивається і все ще посилається на параметр неявного об'єкта.)

Тож ваш приклад, здається, чітко визначений, обумовлений ідеєю, що відновлення виконання не підпадає під ті самі правила, що і оригінальне виклик. Зауважте, що посилання на об'єкт закриття може бути звисаючим, але це не має жодного доступу між призупиненням та відновленням.


Ви маєте на увазі "відновлення та завершення" наприкінці?
Девіс Оселедець

@DavisHerring Ні, я мав на увазі конкретно в межах "поза" часових рамок, де не ясно, чи може посилання може бути призначене для нового посилання тощо, що вимагатиме реального об'єкта. Той факт, що до нього не звертаються приховано, важливо, щоб це не було UB
Columbo

Але недостатньо залишити звисаючу посилання в спокої до відновлення; ви повинні залишити його в спокої ( наприклад , в тілі лямбда) назавжди - на весь час його життя, який є до завершення. Тож, можливо, це має бути "припинення та завершення".
Девіс Оселедець

@DavisHerring Я спеціально згадав про цей інтервал, оскільки в нашому прикладі ми знаємо, що інший є безпечним.
Коламбо

Впевнений; Я просто вважаю формулювання заплутаним. Можливо, ніхто інший цього не робить.
Девіс Оселедець
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.