Ist es UB, eine Elementfunktionskoroutine eines Objekts wieder aufzunehmen, dessen Lebensdauer abgelaufen ist?

9

Diese Frage ergibt sich aus diesem Kommentar: Erklärung der Lambda-Lebensdauer für C ++ 20-Coroutinen

zu diesem Beispiel:

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

Die Frage ist also, ob die Ausführung der von zurückgegebenen Coroutine zu fooUB führen würde.

Das "Aufrufen" einer Mitgliedsfunktion (nach Ablauf der Lebensdauer des Objekts) lautet UB: http://eel.is/c++draft/basic.life#6.2

... jeder Zeiger, der die Adresse des Speicherorts darstellt, an dem sich das Objekt befindet oder befand, darf nur in begrenztem Umfang verwendet werden. [...] Das Programm hat ein undefiniertes Verhalten, wenn:

[...]

- Der Zeiger wird verwendet, um auf ein nicht statisches Datenelement zuzugreifen oder eine nicht statische Elementfunktion des Objekts aufzurufen , oder

In diesem Beispiel jedoch:

  • Der ()Operator des Lambda wird aufgerufen, solange die Lebensdauer des Lambda noch gültig ist
  • Es wird dann ausgesetzt,
  • dann wird das Lambda zerstört,
  • und dann wird die Elementfunktion (Operator ()) irgendwann danach wieder aufgenommen.

Wird diese Wiederaufnahme als undefiniertes Verhalten betrachtet?

Mike Lui
quelle
2
Möglicherweise ist die folgende Antwort relevant. Stackoverflow.com/a/60495359/12345656 Es scheint ziemlich anders zu sein, aber es handelt sich auch um eine Elementfunktion , bei der der thisZeiger ungültig wird. Beachten Sie auch die Diskussion in den Kommentaren.
n314159

Antworten:

2

[dcl.fct.def.coroutine] p3 :

Das Versprechen Typ eines Koroutine ist std::coroutine_traits<R, P1, ..., Pn>::promise_type, wo Rder Typ der Funktionsrückgabe ist, und P1 ... Pnsind die Folge der Typen der Funktionsparameter, durch die Art des impliziten Objektparameter voraus (12.4.1) , wenn die Koroutine eine nicht-statisch Mitgliedsfunktion.

Der implizite Objektparameter ist in Ihrem Beispiel eine konstante Referenz, und daher baumelt diese Referenz, wenn die Ausführung fortgesetzt wird, nachdem das Abschlussobjekt zerstört wurde.

In Anbetracht der Tatsache, dass Objekte während der Ausführung einer Elementfunktion zerstört werden, ist dies in der Tat per se in Ordnung, und kein anderer als der Standard selbst impliziert dies in [basic]. :

Bevor die Lebensdauer eines Objekts begonnen hat, aber nachdem der Speicher, den das Objekt belegen wird, zugewiesen wurde, oder nachdem die Lebensdauer eines Objekts beendet wurde und bevor der Speicher, den das Objekt belegt, wiederverwendet oder freigegeben wird, ein Zeiger, der die Adresse von darstellt Der Speicherort, an dem sich das Objekt befindet oder befand, darf nur in begrenztem Umfang verwendet werden. [...]

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

(NB: Die obige UB ist weil die implizite this , dass das implizite Objekt nicht gewaschen wird und sich immer noch auf den impliziten Objektparameter bezieht.)

Ihr Beispiel scheint also klar definiert zu sein, vorausgesetzt, dass die Wiederaufnahme der Ausführung nicht denselben Regeln unterliegt wie ein ursprünglicher Aufruf. Beachten Sie, dass der Verweis auf das Schließobjekt möglicherweise baumelt, aber zwischen Suspendierung und Wiederaufnahme in keiner Weise darauf zugegriffen wird.

Columbo
quelle
Meinen Sie am Ende „Wiederaufnahme und Fertigstellung“?
Davis Herring
@DavisHerring Nein, ich meinte speziell innerhalb dieses "äußeren" Zeitrahmens, in dem nicht klar ist, ob die Referenz einer neuen Referenz usw. zugewiesen werden könnte, die ein reales Objekt erfordern würde. Die Tatsache, dass nicht auf versteckte Weise auf die Referenz zugegriffen wird, ist wichtig, damit dies nicht UB
Columbo
Es reicht jedoch nicht aus, die baumelnde Referenz bis zur Wiederaufnahme in Ruhe zu lassen. Sie müssen es für immer in Ruhe lassen ( z. B. im Lambda-Körper) - für den Rest seines Lebens, bis zur Fertigstellung. Vielleicht sollte es also "Suspendierung und Fertigstellung" sein.
Davis Herring
@DavisHerring Ich habe dieses Intervall ausdrücklich erwähnt, weil wir in unserem Beispiel wissen, dass das andere sicher ist.
Columbo
Sicher; Ich finde den Wortlaut nur verwirrend. Vielleicht tut es sonst niemand.
Davis Herring