web-dev-qa-db-fra.com

Comment utiliser boost :: facultatif

J'essaie d'utiliser boost::optional comme ci-dessous. 

#include <iostream>
#include <string>

#include <boost/optional.hpp>

struct myClass
{
   int myInt;
   void setInt(int input) { myInt = input; }
   int  getInt(){return myInt; }
};

boost::optional<myClass> func(const std::string &str)
{
   boost::optional<myClass> value;
   if(str.length() > 5)
   {
      // If greater than 5 length string. Set value to 10
      value.get().setInt(10);
   }
   else if (str.length() < 5)
   {
      // Else set it to 0
      value.get().setInt(0);
   }
   else
   {
      // If it is 5 set the value to 5
      value.get().setInt(5);
   }

   return value;
}


int main()
{
   boost::optional<myClass> v1 = func("3124");
   boost::optional<myClass> v2 = func("helloWorld");
   boost::optional<myClass> v3 = func("hello");

   if (v1)
       std::cout << "v1 is valid" << std::endl;
   else
       std::cout << "v1 is not valid" << std::endl;

   if (v2)
       std::cout << "v2 is valid" << std::endl;
   else
      std::cout << "v3 is not valid" << std::endl;

   if (v3)
      std::cout << "v3 is valid" << std::endl;
   else
      std::cout << "v3 is not valid" << std::endl;

  return 0;
 }

Je reçois l'erreur suivante

prog.exe: /usr/local/boost-1.55.0/include/boost/optional/optional.hpp:631: boost :: optional :: type de référence boost :: optional :: get () [avec T = Ma classe; boost :: optional :: reference_type = myClass &]: Assertion `this-> is_initialized () 'a échoué.

Vraisemblablement, la variable facultative n’est pas initialisée correctement. Comment le faire correctement?

EDIT :: Vous avez de très bonnes réponses, juste quelques questions supplémentaires 1. Est-ce une bonne idée d'utiliser make_optional à la fin de la fonction 'func' et de la retourner? Aussi 2. Je pensais assigner boost::none pour souligner que je n’ai aucune valeur à attribuer et c’est pourquoi boost::none. Mais pas sûr que ce soit valable? 

24
polapts

Un boost::optional construit par défaut est vide - il ne contient pas de valeur, vous ne pouvez donc pas appeler get() dessus. Vous devez l'initialiser avec une valeur valide:

boost::optional<myClass> value = myClass();

Vous pouvez également utiliser une fabrique in-place pour éviter l'initialisation de la copie (mais la copie sera probablement éliminée de toute façon); Cependant, je n'ai aucune expérience dans ce domaine et je ne peux donc pas donner d'exemple.


En remarque, vous pouvez utiliser -> à la place de get(), comme ceci:

value->setInt(10);

Mais ce n'est qu'une question de préférence stylistique, les deux sont également valables.

23
Angew

Deux approches faciles:

boost::optional<myClass> func(const std::string &str)
{
  boost::optional<myClass> value;
  if(str.length() > 5) // If greater than 5 length string. Set value to 10
    value = 10;
  else if (str.length() < 5) // Else set it to 0
    value = 0;
  else // If it is 5 set the value to 5
    value = 5;

  return value;
}

boost::optional<myClass> func(const std::string &str)
{
  if(str.length() > 5) // If greater than 5 length string. Set value to 10
    return 10;
  else if (str.length() < 5) // Else set it to 0
    return 0;
  else // If it is 5 set the value to 5
    return 5;
}

notez que renvoyer une optional à partir d'une fonction qui ne renvoie jamais une option vide est une mauvaise idée.

optional se comporte comme un pointeur sur l'accès en lecture - vous ne pouvez lire la valeur de celle-ci que si vous avez déjà vérifié qu'il y a quelque chose à lire. Vous pouvez vérifier s'il y a quelque chose à lire en faisant bool something_to_read = opt;.

Vous pouvez cependant lui écrire à tout moment. S'il n'y a rien là-bas, cela crée quelque chose. S'il y a quelque chose là, ça écrase.

.get() est une lecture, pas une écriture, une opération. (il "lit" la référence) Son utilisation est sûre uniquement lorsque la optional est activée et contient des données. De manière confuse, vous pouvez écrire dans la valeur de retour "read access" .get(), car il s'agit d'une référence non const.

Alors peut-être que "lire" et "écrire" sont de mauvais mots à utiliser. :)

Il est parfois utile de penser à facultatif en tant que valeur et pointeur mélangés. Il existe un pointeur potentiellement nul vers un tampon de mémoire appartenant à l'utilisateur qui peut ou non contenir une copie du type.

Si le pointeur à l'intérieur de facultatif est null, la mémoire tampon n'est pas initialisée. S'il pointe vers le tampon, celui-ci est initialisé.

.get() supprime ce pointeur et renvoie la référence résultante sans vérification. = vérifie le pointeur. S'il est nul, il effectue une copie-construction du rhs dans le tampon et le positionne. Sinon, il ne fait qu'attribuer à la mémoire tampon.

(Le pointeur est conceptuel: généralement implémenté sous la forme d'un drapeau bool).

Je trouve que l'utilisation de *optional est meilleure que optional.get(), car le "vous devez vérifier avant de déréférencer" est plus évident avec l'opérateur de déréférencement.

Comment le faire correctement?

boost::optional<myClass> func(const std::string &str)
{
    if(str.length() > 5)
        return myClass{10};
    if(str.length() < 5)
        return myClass{0};
    return myClass{5};
}

En remarque, ce code n'a pas besoin de boost :: optional, car il n'y a pas de branche de code qui retourne un objet vide (c'est sémantiquement équivalent au retour d'une instance myClass).

Pour retourner un optionnel vide, utilisez ceci:

boost::optional<myClass> func(const std::string &str)
{
    if(str.length() > 5)
        return myClass{10};
    if(str.length() < 5)
        return myClass{0};
    return boost::none; // return empty object
}

Code client idiomatique (ne pré-initialisez pas vos valeurs):

int main()
{
    if (auto v1 = func("3214"))
        // use *v1 to access value
        std::cout << "v1 is valid" << std::endl;
    else
        std::cout << "v1 is not valid" << std::endl;

    return 0;
}
7
utnapistim
boost::optional<myClass> func(const std::string &str)
{
    boost::optional<myClass> value; //not init is invalid
    if(str.length() > 5)       // If greater than 5 length string. Set value to 10
        value = 10;
    else if (str.length() < 5) // Else set it to 0
        value = 0;

    return value;
}


v1 is valid
v2 is valid
v3 is not valid

selon boost, ctor optionnel créera un obj optionnel invalide

optional<T> def ; //not initalize with a obj T
assert ( !def ) ;
1
camino