2017-07-29 1 views
13

J'ai souvent besoin d'utiliser le type en option pour les fonctions:Valeur de retour en option avec: opérateur

std::optional<int32_t> get(const std::string& field) 
{ 
    auto it = map.find(field); 
    if (it != map.end()) return it->second; 
    return {}; 
} 

est-il un moyen de retourner la valeur facultative dans une ligne? par exemple. ceci:

std::optional<int32_t> get(const std::string& field) 
{ 
    auto it = map.find(field); 
    return it != map.end() ? it->second : {}; 
} 

résultats dans l'erreur

error: expected primary-expression before '{' token 
return it != map.end() ? it->second : {}; 
            ^
+0

@ tobi303 '{}' n'a pas été analysé comme une expression. – MiP

+2

Clang donne un message d'erreur beaucoup plus agréable: "liste d'initialisation ne peut pas être utilisé sur le côté droit de l'opérateur '?" " – emlai

Répondre

20

Vous pouvez explicitement envelopper le retour de valeur unidirectionnelle dans un std::optional et revenir au constexprstd::nullopt pour le retour sans valeur.

std::nullopt:

std::nullopt est une constante de type std::nullopt_t qui est utilisé pour indiquer le type en option avec l'état non initialisé.

...

std::nullopt_t:

std::nullopt_t est un type de classe vide utilisé pour indiquer le type en option avec l'état non initialisée. En particulier, std::optional a un constructeur avec nullopt_t comme argument unique, ce qui crée un optionnel qui ne contient pas de valeur.

Avec cette approche, la vraie clause de l'appel de l'opérateur ternaire renvoie explicitement un std::optional avec une certaine valeur, de sorte que le compilateur peut déduire le paramètre de modèle/type enveloppé (dans cet exemple: int32_t) du type de la valeur enveloppée fournie, ce qui signifie que vous n'avez pas besoin de le spécifier explicitement.

appliquée à votre exemple:

return it != map.end() ? std::optional(it->second) : std::nullopt; 

// alternatively 
return it != map.end() ? std::make_optional(it->second) : std::nullopt; 
19
return it != map.end() ? it->second : std::optional<int32_t>{}; 

devrait faire l'affaire.

Le compilateur doit déduire le type de résultat de l'expression ternaire des deux opérandes, mais il n'y a aucun moyen il peut déduire std::optional<int32_t> de int32_t et {}. D'autre part, ont le type commun désiré std::optional<int32_t>.


amusant connexes fait: Vous pouvez éviter de répéter le type avec déduction de type de retour automatique:

auto get(const std::string& field) 
{ 
    auto it = map.find(field); 
    return it != map.end() ? it->second : std::optional<int32_t>{}; 
} 

Selon les préférences, vous pouvez bien sûr déduire également l'argument de modèle pour la std::optional de it->second avec decltype réduire davantage la répétition.

+0

Selon la façon dont' map' est déclaré (et la mesure dans laquelle 'int32_t' pourrait varier de façon plausible au moment de la compilation), vous pourriez aller encore plus loin et le calquer. – Kevin