2
votes

Existe-t-il un idiome moins verbeux pour décompresser un optionnel en C ++?

Dans le projet sur lequel je travaille actuellement, je me retrouve à écrire beaucoup de code qui ressemble à ce qui suit, où get_optional_foo renvoie un std :: facultatif:

//...
auto maybe_foo = get_optional_foo(quux, ...)
if (!maybe_foo.has_value())
    return {};
auto foo = maybe_foo.value()
//...
// continue on, doing things with foo...

Je veux renflouer la fonction si j'obtiens une option nulle; sinon, je souhaite attribuer une variable non facultative à la valeur. J'ai commencé à utiliser la convention de nommer l'option avec un maybe_ préfixe mais me demande s'il y a une façon de le faire de telle sorte que je ne ai pas besoin d'utiliser pour l'option temporaire du tout? Cette variable ne sera jamais utilisée que pour vérifier une option nulle et déréférencer s'il y a une valeur.


5 commentaires

Pourquoi ne pas simplement le traiter comme un pointeur? auto foo = get_optional_foo(quux, ...) if (!foo) return {}; ... foo->some_member;


la syntaxe de la flèche fonctionne avec std :: facultatif?


Oui. Il est fait pour émuler un pointeur sauf aucune allocation dynamique. Vous pouvez également le déréférencer.


Je ne savais pas cela. Écrivez-le comme une réponse et je l'accepterai.


Si vous en avez beaucoup, ajoutez .value () après l'appel et attrapez std :: bad_optional_access


3 Réponses :


1
votes

Le plus court auquel je puisse penser:

try {
  auto &foo = get_optional_foo(quux, ...).value();
  auto &bar = get_optional_bar(...).value();
  ...
} catch (std::bad_optional_access &e) {
  return {};
}

Si vous avez plusieurs options dans la fonction et qu'il est très peu probable qu'elles soient vides, vous pouvez envelopper le tout avec un try - catch .

auto maybe_foo = get_optional_foo(quux, ...)
if (!maybe_foo) return {};

auto &foo = *maybe_foo; // alternatively, use `*maybe_foo` below


6 commentaires

Si vous voulez plus court, vous pouvez écrire return maybe_foo ? *maybe_foo : {} .


Si nous voulions revenir, nous pourrions simplement utiliser return maybe_foo.value_or({}); bien que.


@ypnos Clang se plaint de ceci: error: initializer list cannot be used on the right hand side of operator ':' .


@Kostas Cela ferait un appel de constructeur par défaut inutile si la condition est fausse.


@HolyBlackCat Pour value_or({}) ? Je suis sûr que tout compilateur décent est heureux d'optimiser cela avec une élision de copie.


Ce que je dis, c'est que si l'option facultative stocke déjà une valeur, {} construira toujours par défaut un nouvel objet, même si ce n'est pas nécessaire. Si le corps du constructeur par défaut n'est pas visible, le compilateur doit supposer qu'il pourrait avoir des effets secondaires et doit l'appeler.



3
votes

Vous n'avez pas besoin d'un objet intermédiaire. std::optional prend en charge une interface de pointeur pour y accéder afin que vous puissiez simplement l'utiliser comme:

//...
auto foo = get_optional_foo(quux, ...)
if (!foo)
    return {};
//...
foo->some_member;
(*foo).some_member;


0 commentaires

1
votes

Légèrement différent de ce que vous demandez, mais considérez:

  if (auto foo = get_optional_foo(1)) {
    // ...
    return foo->x;
  } else {
    return {};
  }

Cela place le corps principal de la fonction dans un bloc if() , qui peut être plus lisible.


0 commentaires