Menu
Coddy logo textTech

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.

challenge icon

Défi

Facile

Construisons 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 parseValue qui 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é dans std::optional. Si l'analyse échoue (la chaîne contient des caractères non numériques), renvoyez std::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é Setting utilisant std::variant, qui peut contenir soit un int, soit un double, soit un std::string.

    Enfin, créez une fonction appelée describeSetting qui prend un Setting et 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]
  • DynamicStore.h : créez un magasin clé-valeur simple utilisant std::any.

    Définissez une classe DynamicStore capable de stocker des valeurs de n'importe quel type. Elle doit posséder :

    • Une méthode set qui prend une clé de type chaîne et une valeur std::any, puis les stocke en interne (utilisez une std::map)
    • Une méthode get qui prend une clé et renvoie la valeur std::any (renvoyez un std::any vide si la clé n'existe pas)
    • Une méthode hasKey qui renvoie true si la clé existe
  • main.cpp : lisez trois entrées :
    1. Une chaîne qui peut être un nombre (pour tester std::optional)
    2. Un indicateur de type : int, double ou string
    3. Une valeur correspondant à ce type

    Démontrez les trois types vocabulaire :

    Tout d'abord, utilisez votre fonction parseValue avec la première entrée. Si elle contient une valeur, affichez Parsed: [value]. Si elle est vide, affichez Parse failed.

    Ensuite, créez une variante Setting en fonction de l'indicateur de type. Si le type est int, stockez la valeur comme un entier. S'il s'agit de double, stockez-la comme un double. S'il s'agit de string, stockez-la comme une chaîne. Appelez ensuite describeSetting et 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]. Utilisez std::any_cast<std::string> pour extraire la valeur.

Par exemple, avec les entrées 42, int et 100 :

Parsed: 42
Integer: 100
Status: config_loaded

Avec les entrées hello, double et 3.14 :

Parse failed
Double: 3.14
Status: config_loaded

Avec les entrées -15, string et username :

Parsed: -15
String: username
Status: config_loaded

N'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;
}
quiz iconTestez-vous

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

Entraînez-vous par vous-même : Compilateur C++ en ligne