optional, variant, any
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 89 di 104.
Il C++ moderno fornisce tre tipi del vocabolario negli header <optional>, <variant> e <any> che ti aiutano a gestire valori che potrebbero essere assenti, contenere uno di diversi tipi o memorizzare qualsiasi tipo.
std::optional<T> rappresenta un valore che potrebbe esistere oppure no: è perfetto per le funzioni che potrebbero non riuscire senza generare un'eccezione:
#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; // Nessun valore
}
int main() {
auto result = findIndex("hello", 'l');
if (result.has_value()) {
std::cout << "Found at: " << *result << "\n"; // 2
}
}std::variant<Types...> è un'unione con sicurezza dei tipi che contiene esattamente uno dei tipi specificati in qualsiasi momento:
#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 può contenere un valore di qualsiasi tipo, determinato in fase di esecuzione. Usa std::any_cast per recuperare il valore:
#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";
}Scegli std::optional per i valori nullabili, std::variant quando conosci i possibili tipi in fase di compilazione e std::any solo quando hai davvero bisogno di flessibilità dei tipi a runtime.
Sfida
FacileCreiamo un sistema di configurazione che dimostri la potenza dei moderni tipi vocabolo di C++. Creerai un gestore flessibile delle impostazioni, in grado di gestire valori che potrebbero essere assenti, contenere tipi diversi o richiedere flessibilità di tipo in fase di esecuzione.
Organizzerai il codice in tre file:
ConfigTypes.h: definisci i tipi dei valori di configurazione e le funzioni di supporto.Crea una funzione chiamata
parseValueche accetta una stringa e prova a convertirla in un intero. Se la stringa rappresenta un intero valido, restituisci l'intero racchiuso instd::optional. Se la conversione non riesce (la stringa contiene caratteri non numerici), restituiscistd::nullopt. Per semplicità, considera valida una stringa se contiene solo cifre (e, facoltativamente, un segno meno iniziale).Crea anche un alias di tipo chiamato
Settingusandostd::variant, che possa contenere unint, undoubleoppure unastd::string.Infine, crea una funzione chiamata
describeSettingche accetta unSettinge restituisce una stringa che descrive il tipo contenuto e il suo valore nel formato seguente:- Per int:
Integer: [value] - Per double:
Double: [value] - Per string:
String: [value]
- Per int:
DynamicStore.h: crea un semplice archivio chiave-valore usandostd::any.Definisci una classe
DynamicStoreche possa memorizzare valori di qualsiasi tipo. Dovrebbe avere:- Un metodo
setche accetta una chiave stringa e un valorestd::any, memorizzandoli internamente (usa unastd::map) - Un metodo
getche accetta una chiave e restituisce il valorestd::any(restituisci unastd::anyvuota se la chiave non esiste) - Un metodo
hasKeyche restituisce true se la chiave esiste
- Un metodo
main.cpp: leggi tre valori in input:- Una stringa che potrebbe essere un numero (per provare
std::optional) - Un indicatore di tipo:
int,doubleoppurestring - Un valore corrispondente a quel tipo
Mostra tutti e tre i tipi vocabolo:
Per prima cosa, usa la funzione
parseValuecon il primo valore in input. Se contiene un valore, stampaParsed: [value]. Se è vuoto, stampaParse failed.In secondo luogo, crea una variante
Settingin base all'indicatore di tipo. Se il tipo èint, memorizza il valore come intero. Se èdouble, memorizza il valore come double. Se èstring, memorizza il valore come stringa. Poi chiamadescribeSettinge stampa il risultato.Infine, crea un
DynamicStore, memorizza la stringa"config_loaded"sotto la chiave"status", poi recuperala e stampa:Status: [value]. Usastd::any_cast<std::string>per estrarre il valore.- Una stringa che potrebbe essere un numero (per provare
Per esempio, con i valori in input 42, int e 100:
Parsed: 42
Integer: 100
Status: config_loadedCon i valori in input hello, double e 3.14:
Parse failed
Double: 3.14
Status: config_loadedCon i valori in input -15, string e username:
Parsed: -15
String: username
Status: config_loadedRicordati di includere gli header appropriati: <optional>, <variant>, <any>, <string> e <map>, dove necessario. Usa std::holds_alternative e std::get per lavorare con la variante, e has_value() o la conversione booleana diretta per controllare l'optional.
Provalo tu
#include <iostream>
#include <string>
#include <any>
#include "ConfigTypes.h"
#include "DynamicStore.h"
int main() {
// Leggi tre input
std::string input1; // Stringa che potrebbe essere un numero (per testare std::optional)
std::string typeIndicator; // Indicatore del tipo: int, double o string
std::string value; // Valore corrispondente a quel tipo
std::cin >> input1;
std::cin >> typeIndicator;
std::cin >> value;
// TODO: Parte 1 - Testa std::optional con parseValue
// Usa la funzione parseValue con input1
// Se contiene un valore, stampa "Parsed: [value]"
// Se è vuoto, stampa "Parse failed"
// TODO: Parte 2 - Testa std::variant con Setting
// Crea una variante Setting in base a typeIndicator
// Se il tipo è "int", memorizza il valore come intero
// Se il tipo è "double", memorizza il valore come double
// Se il tipo è "string", memorizza il valore come stringa
// Poi chiama describeSetting e stampa il risultato
// TODO: Parte 3 - Testa std::any con DynamicStore
// Crea un DynamicStore
// Memorizza la stringa "config_loaded" con la chiave "status"
// Recuperala e stampa: "Status: [value]"
// Usa std::any_cast<std::string> per estrarre il valore
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore generico12Funzionalità moderne di C++
Semantica di spostamento e rvaluePerfect forwardingEspressioni lambda in dettagliostd::function e std::bindconstexpr e constevalStructured bindingsoptional, variant, anyEsercitati da solo: Compilatore C++ online