Menu
Coddy logo textTech

optional, variant, any

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 89 von 104.

Modernes C++ bietet drei Vokabular-Typen in den <optional>, <variant> und <any> Headern an, die Ihnen helfen, Werte zu handhaben, die fehlen könnten, einen von mehreren Typen halten oder einen beliebigen Typ speichern können.

std::optional<T> repräsentiert einen Wert, der existieren kann oder auch nicht – ideal für Funktionen, die fehlschlagen könnten, ohne eine Exception auszulösen:

#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;  // Kein Wert
}

int main() {
    auto result = findIndex("hello", 'l');
    if (result.has_value()) {
        std::cout << "Found at: " << *result << "\n";  // 2
    }
}

std::variant<Types...> ist eine typsichere Union, die zu jedem Zeitpunkt genau einen der angegebenen Typen enthält:

#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 kann einen Wert eines beliebigen Typs speichern, der zur Laufzeit bestimmt wird. Verwenden Sie std::any_cast, um den Wert abzurufen:

#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";
}

Wählen Sie std::optional für Nullwerte, std::variant, wenn Sie die möglichen Typen zur Kompilierzeit kennen, und std::any nur dann, wenn Sie wirklich Flexibilität bei den Typen zur Laufzeit benötigen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Konfigurationssystem erstellen, das die Leistungsfähigkeit moderner C++-Vokabular-Typen demonstriert. Sie werden einen flexiblen Einstellungsmanager erstellen, der Werte verarbeitet, die fehlen könnten, verschiedene Typen enthalten können oder Laufzeit-Typflexibilität benötigen.

Sie werden Ihren Code auf drei Dateien verteilen:

  • ConfigTypes.h: Definieren Sie Ihre Konfigurationswerttypen und Hilfsfunktionen.

    Erstellen Sie eine Funktion namens parseValue, die einen String entgegennimmt und versucht, ihn als Ganzzahl zu parsen. Wenn der String eine gültige Ganzzahl darstellt, geben Sie die Ganzzahl verpackt in ein std::optional zurück. Wenn das Parsen fehlschlägt (der String enthält nicht-numerische Zeichen), geben Sie std::nullopt zurück. Betrachten Sie der Einfachheit halber einen String als gültig, wenn er nur Ziffern (und optional ein führendes Minuszeichen) enthält.

    Erstellen Sie außerdem einen Typ-Alias namens Setting unter Verwendung von std::variant, der entweder ein int, ein double oder ein std::string enthalten kann.

    Erstellen Sie schließlich eine Funktion namens describeSetting, die ein Setting entgegennimmt und einen String zurückgibt, der beschreibt, welchen Typ es enthält und welchen Wert es hat, in diesem Format:

    • Für int: Integer: [value]
    • Für double: Double: [value]
    • Für string: String: [value]
  • DynamicStore.h: Erstellen Sie einen einfachen Key-Value-Speicher unter Verwendung von std::any.

    Definieren Sie eine Klasse DynamicStore, die Werte eines beliebigen Typs speichern kann. Sie sollte Folgendes enthalten:

    • Eine Methode set, die einen String-Schlüssel und einen std::any-Wert entgegennimmt und diese intern speichert (verwenden Sie eine std::map)
    • Eine Methode get, die einen Schlüssel entgegennimmt und den std::any-Wert zurückgibt (geben Sie ein leeres std::any zurück, wenn der Schlüssel nicht existiert)
    • Eine Methode hasKey, die true zurückgibt, wenn der Schlüssel existiert
  • main.cpp: Lesen Sie drei Eingaben ein:
    1. Einen String, der eine Zahl sein könnte (zum Testen von std::optional)
    2. Einen Typ-Indikator: int, double oder string
    3. Einen Wert, der diesem Typ entspricht

    Demonstrieren Sie alle drei Vokabular-Typen:

    Verwenden Sie zuerst Ihre Funktion parseValue mit der ersten Eingabe. Wenn sie einen Wert enthält, geben Sie Parsed: [value] aus. Wenn sie leer ist, geben Sie Parse failed aus.

    Zweitens, erstellen Sie eine Setting-Variante basierend auf dem Typ-Indikator. Wenn der Typ int ist, speichern Sie den Wert als Ganzzahl. Wenn double, speichern Sie ihn als Double. Wenn string, speichern Sie ihn als String. Rufen Sie dann describeSetting auf und geben Sie das Ergebnis aus.

    Drittens, erstellen Sie einen DynamicStore, speichern Sie den String "config_loaded" unter dem Schlüssel "status", rufen Sie ihn dann ab und geben Sie aus: Status: [value]. Verwenden Sie std::any_cast<std::string>, um den Wert zu extrahieren.

Zum Beispiel mit den Eingaben 42, int und 100:

Parsed: 42
Integer: 100
Status: config_loaded

Mit den Eingaben hello, double und 3.14:

Parse failed
Double: 3.14
Status: config_loaded

Mit den Eingaben -15, string und username:

Parsed: -15
String: username
Status: config_loaded

Denken Sie daran, die entsprechenden Header einzubinden: <optional>, <variant>, <any>, <string> und <map>, wo sie benötigt werden. Verwenden Sie std::holds_alternative und std::get, um mit Ihrer Variante zu arbeiten, und has_value() oder eine direkte boolesche Konvertierung, um Ihr Optional zu prüfen.

Spickzettel

Modernes C++ bietet drei Vokabular-Typen für den Umgang mit flexiblen Werten:

std::optional<T>

Repräsentiert einen Wert, der existieren kann oder auch nicht. Binden Sie <optional> ein:

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;  // No value
}

auto result = findIndex("hello", 'l');
if (result.has_value()) {
    std::cout << *result;  // Dereference to get value
}

std::variant<Types...>

Eine typsichere Union, die genau einen der angegebenen Typen enthält. Binden Sie <variant> ein:

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

Kann einen Wert eines beliebigen Typs halten, der zur Laufzeit bestimmt wird. Binden Sie <any> ein:

std::any value = 10;
std::cout << std::any_cast<int>(value);

value = std::string("text");
std::cout << std::any_cast<std::string>(value);

Wann man sie verwendet: std::optional für Nullable-Werte, std::variant, wenn die möglichen Typen zur Kompilierzeit bekannt sind, std::any für Typflexibilität zur Laufzeit.

Probier es selbst

#include <iostream>
#include <string>
#include <any>
#include "ConfigTypes.h"
#include "DynamicStore.h"

int main() {
    // Lies drei Eingaben ein
    std::string input1;  // String, der eine Zahl sein könnte (zum Testen von std::optional)
    std::string typeIndicator;  // Typ-Indikator: int, double oder string
    std::string value;  // Wert, der diesem Typ entspricht
    
    std::cin >> input1;
    std::cin >> typeIndicator;
    std::cin >> value;
    
    // TODO: Teil 1 - Teste std::optional mit parseValue
    // Verwende die Funktion parseValue mit input1
    // Wenn sie einen Wert enthält, gib "Parsed: [value]" aus
    // Wenn sie leer ist, gib "Parse failed" aus
    
    
    // TODO: Teil 2 - Teste std::variant mit Setting
    // Erstelle eine Setting-Variante basierend auf typeIndicator
    // Wenn der Typ "int" ist, speichere den Wert als integer
    // Wenn der Typ "double" ist, speichere den Wert als double
    // Wenn der Typ "string" ist, speichere den Wert als string
    // Rufe dann describeSetting auf und gib das Ergebnis aus
    
    
    // TODO: Teil 3 - Teste std::any mit DynamicStore
    // Erstelle einen DynamicStore
    // Speichere den String "config_loaded" unter dem Schlüssel "status"
    // Rufe ihn ab und gib aus: "Status: [value]"
    // Verwende std::any_cast<std::string>, um den Wert zu extrahieren
    
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung