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 de vocabulaire du C++ moderne. Vous allez créer un gestionnaire de paramètres flexible qui gère des valeurs pouvant être absentes, pouvant contenir différents types, ou nécessitant une flexibilité de type à l'exécution.
Vous organiserez votre code sur trois fichiers :
ConfigTypes.h: Définissez vos types de valeurs de configuration et vos fonctions utilitaires.Créez une fonction nommée
parseValuequi prend une chaîne de caractères et tente de l'analyser comme un entier. Si la chaîne représente un entier valide, retournez l'entier enveloppé dans unstd::optional. Si l'analyse échoue (la chaîne contient des caractères non numériques), retournezstd::nullopt. Pour simplifier, considérez une chaîne comme valide si elle ne contient que des chiffres (et éventuellement un signe moins au début).Créez également un alias de type nommé
Settingutilisantstd::variantpouvant contenir soit unint, undouble, ou unstd::string.Enfin, créez une fonction nommée
describeSettingqui prend unSettinget retourne une chaîne décrivant le type qu'il contient et 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
DynamicStorequi peut stocker des valeurs de n'importe quel type. Elle doit avoir :- Une méthode
setqui prend une clé de type chaîne et une valeurstd::any, en les stockant en interne (utilisez unstd::map) - Une méthode
getqui prend une clé et retourne la valeurstd::any(retournez unstd::anyvide si la clé n'existe pas) - Une méthode
hasKeyqui retourne true si la clé existe
- Une méthode
main.cpp: Lisez trois entrées :- Une chaîne qui pourrait être un nombre (pour tester
std::optional) - Un indicateur de type :
int,double, oustring - Une valeur correspondant à ce type
Démontrez les trois types de vocabulaire :
Premièrement, utilisez votre fonction
parseValueavec la première entrée. Si elle contient une valeur, affichezParsed: [value]. Si elle est vide, affichezParse failed.Deuxièmement, créez un variant
Settingbasé sur l'indicateur de type. Si le type estint, stockez la valeur en tant qu'entier. Si c'estdouble, stockez-la en tant que double. Si c'eststring, stockez-la en tant que chaîne. Appelez ensuitedescribeSettinget affichez le résultat.Troisièmement, 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 pourrait ê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ù c'est nécessaire. Utilisez std::holds_alternative et std::get pour travailler avec votre variant, et has_value() ou une conversion booléenne directe pour vérifier votre optionnel.
Aide-mémoire
Le C++ moderne propose trois types de vocabulaire pour gérer les valeurs flexibles :
std::optional<T>
Représente une valeur qui peut exister ou non. Incluez <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
}
auto result = findIndex("hello", 'l');
if (result.has_value()) {
std::cout << *result; // Déréférencer pour obtenir la valeur
}std::variant<Types...>
Une union de types sécurisée qui contient exactement l'un des types spécifiés. Incluez <variant> :
std::variant<int, double, std::string> data;
data = 42;
std::cout << std::get<int>(data);
data = "hello";
if (std::holds_alternative<std::string>(data)) {
std::cout << std::get<std::string>(data);
}std::any
Peut contenir une valeur de n'importe quel type, déterminée à l'exécution. Incluez <any> :
std::any value = 10;
std::cout << std::any_cast<int>(value);
value = std::string("text");
std::cout << std::any_cast<std::string>(value);Quand les utiliser : std::optional pour les valeurs pouvant être nulles, std::variant lorsque les types possibles sont connus à la compilation, std::any pour une flexibilité de type à l'exécution.
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 externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers 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 d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'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 de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique12Fonctionnalités du C++ moderne
Sémantique de mouvement et RvaluesTransfert parfaitExpressions Lambda en profondeurstd::function et std::bindconstexpr et constevalLiaisons structuréesoptional, variant, any