optional, variant, any
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 89 sur 104.
Le C++ moderne fournit trois types de vocabulaire dans les en-têtes <optional>, <variant> et <any> qui vous aident à gérer des valeurs qui pourraient être absentes, contenir l'un de plusieurs types ou stocker n'importe quel type.
std::optional<T> représente une valeur qui peut ou non exister - parfait pour les fonctions qui pourraient échouer sans lever d'exception :
#include <iostream>
#include <optional>
std::optional<int> findIndex(const std::string& str, char c) {
for (size_t i = 0; i < str.size(); ++i) {
if (str[i] == c) return i;
}
return std::nullopt; // Aucune valeur
}
int main() {
auto result = findIndex("hello", 'l');
if (result.has_value()) {
std::cout << "Found at: " << *result << "\n"; // 2
}
}std::variant<Types...> est une union de types sécurisée (type-safe union) qui contient exactement l'un des types spécifiés à tout moment :
#include <iostream>
#include <variant>
#include <string>
int main() {
std::variant<int, double, std::string> data;
data = 42;
std::cout << std::get<int>(data) << "\n";
data = "hello";
if (std::holds_alternative<std::string>(data)) {
std::cout << std::get<std::string>(data) << "\n";
}
}std::any peut contenir une valeur de n'importe quel type, déterminée au moment de l'exécution. Utilisez std::any_cast pour récupérer la valeur :
#include <iostream>
#include <any>
int main() {
std::any value = 10;
std::cout << std::any_cast<int>(value) << "\n";
value = std::string("text");
std::cout << std::any_cast<std::string>(value) << "\n";
}Choisissez std::optional pour les valeurs nullables, std::variant lorsque vous connaissez les types possibles au moment de la compilation, et std::any uniquement lorsque vous avez réellement besoin de flexibilité de type à l'exécution.
Défi
FacileConstruisons un système de configuration qui démontre la puissance des types vocabulaire modernes de C++. Vous créerez un gestionnaire de paramètres flexible qui prend en charge les valeurs qui peuvent être absentes, contenir différents types ou nécessiter une flexibilité de type à l'exécution.
Vous organiserez votre code dans trois fichiers :
ConfigTypes.h: définissez vos types de valeurs de configuration et vos fonctions utilitaires.Créez une fonction appelée
parseValuequi prend une chaîne et tente de l'analyser comme un entier. Si la chaîne représente un entier valide, renvoyez l'entier enveloppé dansstd::optional. Si l'analyse échoue (la chaîne contient des caractères non numériques), renvoyezstd::nullopt. Pour simplifier, considérez qu'une chaîne est valide si elle ne contient que des chiffres (et éventuellement un signe moins au début).Créez également un alias de type appelé
Settingutilisantstd::variant, qui peut contenir soit unint, soit undouble, soit unstd::string.Enfin, créez une fonction appelée
describeSettingqui prend unSettinget renvoie une chaîne décrivant le type qu'il contient ainsi que sa valeur, dans ce format :- Pour int :
Integer: [value] - Pour double :
Double: [value] - Pour string :
String: [value]
- Pour int :
DynamicStore.h: créez un magasin clé-valeur simple utilisantstd::any.Définissez une classe
DynamicStorecapable de stocker des valeurs de n'importe quel type. Elle doit posséder :- Une méthode
setqui prend une clé de type chaîne et une valeurstd::any, puis les stocke en interne (utilisez unestd::map) - Une méthode
getqui prend une clé et renvoie la valeurstd::any(renvoyez unstd::anyvide si la clé n'existe pas) - Une méthode
hasKeyqui renvoie true si la clé existe
- Une méthode
main.cpp: lisez trois entrées :- Une chaîne qui peut être un nombre (pour tester
std::optional) - Un indicateur de type :
int,doubleoustring - Une valeur correspondant à ce type
Démontrez les trois types vocabulaire :
Tout d'abord, utilisez votre fonction
parseValueavec la première entrée. Si elle contient une valeur, affichezParsed: [value]. Si elle est vide, affichezParse failed.Ensuite, créez une variante
Settingen fonction de l'indicateur de type. Si le type estint, stockez la valeur comme un entier. S'il s'agit dedouble, stockez-la comme un double. S'il s'agit destring, stockez-la comme une chaîne. Appelez ensuitedescribeSettinget affichez le résultat.Enfin, créez un
DynamicStore, stockez la chaîne"config_loaded"sous la clé"status", puis récupérez-la et affichez :Status: [value]. Utilisezstd::any_cast<std::string>pour extraire la valeur.- Une chaîne qui peut être un nombre (pour tester
Par exemple, avec les entrées 42, int et 100 :
Parsed: 42
Integer: 100
Status: config_loadedAvec les entrées hello, double et 3.14 :
Parse failed
Double: 3.14
Status: config_loadedAvec les entrées -15, string et username :
Parsed: -15
String: username
Status: config_loadedN'oubliez pas d'inclure les en-têtes appropriés : <optional>, <variant>, <any>, <string> et <map> là où cela est nécessaire. Utilisez std::holds_alternative et std::get pour travailler avec votre variante, ainsi que has_value() ou une conversion booléenne directe pour vérifier votre valeur optionnelle.
Essayez vous-même
#include <iostream>
#include <string>
#include <any>
#include "ConfigTypes.h"
#include "DynamicStore.h"
int main() {
// Lire trois entrées
std::string input1; // Chaîne qui pourrait être un nombre (pour tester std::optional)
std::string typeIndicator; // Indicateur de type : int, double, ou string
std::string value; // Valeur correspondant à ce type
std::cin >> input1;
std::cin >> typeIndicator;
std::cin >> value;
// TODO: Partie 1 - Tester std::optional avec parseValue
// Utiliser la fonction parseValue avec input1
// S'il contient une valeur, afficher "Parsed: [value]"
// S'il est vide, afficher "Parse failed"
// TODO: Partie 2 - Tester std::variant avec Setting
// Créer un variant Setting basé sur typeIndicator
// Si le type est "int", stocker la valeur en tant qu'entier
// Si le type est "double", stocker la valeur en tant que double
// Si le type est "string", stocker la valeur en tant que chaîne
// Ensuite, appeler describeSetting et afficher le résultat
// TODO: Partie 3 - Tester std::any avec DynamicStore
// Créer un DynamicStore
// Stocker la chaîne "config_loaded" sous la clé "status"
// La récupérer et afficher : "Status: [value]"
// Utiliser std::any_cast<std::string> pour extraire la valeur
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d’accès en C++Spécificateurs d’accès en profondeurMasquage de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l’opérateur d’affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif – Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur générique12Fonctionnalités modernes de C++
Sémantique de déplacement et rvaluesTransfert parfaitExpressions lambda en profondeurstd::function et std::bindconstexpr et constevalLiaisons structuréesoptional, variant, anyEntraînez-vous par vous-même : Compilateur C++ en ligne