Est-ce UB de reprendre une fonction membre coroutine d'un objet dont la durée de vie est terminée?

9

Cette question découle de ce commentaire: explication de la durée de vie Lambda pour les coroutines C ++ 20

concernant cet exemple:

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

La question est donc de savoir si l'exécution de la coroutine renvoyée par fooentraînerait UB.

"Appeler" une fonction membre (après la fin de la durée de vie de l'objet) est UB: http://eel.is/c++draft/basic.life#6.2

... tout pointeur qui représente l'adresse de l'emplacement de stockage où l'objet sera ou était situé peut être utilisé, mais uniquement de manière limitée. [...] Le programme a un comportement indéfini si:

[...]

- le pointeur est utilisé pour accéder à un membre de données non statique ou appeler une fonction membre non statique de l'objet , ou

Cependant, dans cet exemple:

  • l' ()opérateur du lambda est appelé alors que la durée de vie du lambda est toujours valide
  • Il est alors suspendu,
  • alors la lambda est détruite,
  • puis la fonction membre (opérateur ()) est reprise à un moment donné par la suite.

Cette reprise est-elle considérée comme un comportement indéfini?

Mike Lui
la source
2
Peut-être que la réponse suivante est pertinente stackoverflow.com/a/60495359/12345656 Cela semble assez différent, mais il s'agit également d'une fonction membre pendant laquelle l'exécution du thispointeur est invalidée. Considérez également la discussion dans les commentaires.
n314159

Réponses:

2

[dcl.fct.def.coroutine] p3 :

Le type de promesse d'une coroutine est std::coroutine_traits<R, P1, ..., Pn>::promise_type, où Rest le type de retour de la fonction, et P1 ... Pnsont la séquence de types des paramètres de fonction, précédée du type du paramètre d'objet implicite (12.4.1) si la coroutine est un non-statique fonction membre.

Le paramètre d'objet implicite est dans votre exemple une référence const, et donc cette référence sera suspendue lorsque l'exécution reprendra après que l'objet de fermeture a été détruit.

Cependant, sur la note des objets détruits lors de l'exécution d'une fonction membre, c'est bien en soi, et rien d'autre que la norme elle-même n'implique cela dans [de base] :

Avant le début de la durée de vie d'un objet, mais après que le stockage que l'objet va occuper a été alloué ou, après la fin de la durée de vie d'un objet et avant que le stockage que l'objet occupé est réutilisé ou libéré, tout pointeur représentant l'adresse de l'emplacement de stockage où l'objet sera ou était situé peut être utilisé, mais uniquement de manière limitée. [...]

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

(NB: l'UB ci-dessus est parce que l'implicite this n'est pas blanchi et fait toujours référence au paramètre d'objet implicite.)

Votre exemple semble donc bien défini, conditionnel à l'idée que la reprise de l'exécution ne relève pas des mêmes règles qu'une invocation d'origine. Notez que la référence à l'objet de fermeture peut être suspendue, mais il n'y a aucun accès entre la suspension et la reprise.

Columbo
la source
Voulez-vous dire «reprise et achèvement» à la fin?
Davis Herring
@DavisHerring Non, je voulais dire spécifiquement dans ce laps de temps "extérieur", où il n'est pas clair si la référence pourrait être affectée à une nouvelle référence, etc. qui nécessiterait un objet réel. Le fait que la référence ne soit pas accessible de manière cachée est important pour que cela ne soit pas UB
Columbo
Mais il ne suffit pas de laisser la référence suspendue seule jusqu'à la reprise; vous devez le laisser seul ( par exemple , dans le corps lambda) pour toujours - pour le reste de sa vie, c'est-à-dire jusqu'à l'achèvement. Alors peut-être que ce devrait être «suspension et achèvement».
Davis Herring
@DavisHerring J'ai spécifiquement mentionné cet intervalle, car dans notre exemple, nous savons que l'autre est sûr.
Columbo
Sûr; Je trouve juste le libellé déroutant. Peut-être que personne d'autre ne le fait.
Davis Herring