optional, variant, any
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 89 de 104.
O C++ moderno fornece três tipos de vocabulário nos cabeçalhos <optional>, <variant> e <any> que ajudam você a lidar com valores que podem estar ausentes, conter um de vários tipos ou armazenar qualquer tipo.
std::optional<T> representa um valor que pode ou não existir - perfeito para funções que podem falhar sem lançar uma exceção:
#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; // No value
}
int main() {
auto result = findIndex("hello", 'l');
if (result.has_value()) {
std::cout << "Found at: " << *result << "\n"; // 2
}
}std::variant<Types...> é uma união com segurança de tipos que contém exatamente um dos tipos especificados a qualquer 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 pode conter um valor de qualquer tipo, determinado em tempo de execução. Use std::any_cast para recuperar o valor:
#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";
}Escolha std::optional para valores anuláveis, std::variant quando você conhece os tipos possíveis em tempo de compilação, e std::any apenas quando você realmente precisar de flexibilidade de tipo em tempo de execução.
Desafio
FácilVamos criar um sistema de configuração que demonstre o poder dos modernos tipos de vocabulário do C++. Você criará um gerenciador de configurações flexível que lida com valores que podem estar ausentes, conter tipos diferentes ou precisar de flexibilidade de tipos em tempo de execução.
Você organizará seu código em três arquivos:
ConfigTypes.h: Defina seus tipos de valores de configuração e funções auxiliares.Crie uma função chamada
parseValueque receba uma string e tente analisá-la como um inteiro. Se a string representar um inteiro válido, retorne o inteiro encapsulado emstd::optional. Se a análise falhar (a string contiver caracteres não numéricos), retornestd::nullopt. Para simplificar, considere uma string válida se ela contiver apenas dígitos (e, opcionalmente, um sinal de menos inicial).Crie também um alias de tipo chamado
Settingusandostd::variant, que possa conter umint, umdoubleou umastd::string.Por fim, crie uma função chamada
describeSettingque receba umSettinge retorne uma string descrevendo qual tipo ele contém e seu valor neste formato:- Para int:
Integer: [value] - Para double:
Double: [value] - Para string:
String: [value]
- Para int:
DynamicStore.h: Crie um armazenamento simples de chave-valor usandostd::any.Defina uma classe
DynamicStoreque possa armazenar valores de qualquer tipo. Ela deve ter:- Um método
setque receba uma chave string e um valorstd::any, armazenando-os internamente (use umstd::map) - Um método
getque receba uma chave e retorne o valorstd::any(retorne umstd::anyvazio se a chave não existir) - Um método
hasKeyque retorne true se a chave existir
- Um método
main.cpp: Leia três entradas:- Uma string que pode ser um número (para testar
std::optional) - Um indicador de tipo:
int,doubleoustring - Um valor correspondente a esse tipo
Demonstre os três tipos de vocabulário:
Primeiro, use sua função
parseValuecom a primeira entrada. Se ela contiver um valor, imprimaParsed: [value]. Se estiver vazia, imprimaParse failed.Segundo, crie uma variante
Settingcom base no indicador de tipo. Se o tipo forint, armazene o valor como um inteiro. Se fordouble, armazene-o como um double. Se forstring, armazene-o como uma string. Em seguida, chamedescribeSettinge imprima o resultado.Terceiro, crie um
DynamicStore, armazene a string"config_loaded"sob a chave"status", depois recupere-a e imprima:Status: [value]. Usestd::any_cast<std::string>para extrair o valor.- Uma string que pode ser um número (para testar
Por exemplo, com as entradas 42, int e 100:
Parsed: 42
Integer: 100
Status: config_loadedCom as entradas hello, double e 3.14:
Parse failed
Double: 3.14
Status: config_loadedCom as entradas -15, string e username:
Parsed: -15
String: username
Status: config_loadedLembre-se de incluir os cabeçalhos apropriados: <optional>, <variant>, <any>, <string> e <map> onde necessário. Use std::holds_alternative e std::get para trabalhar com sua variante, e has_value() ou uma conversão booleana direta para verificar seu optional.
Experimente você mesmo
#include <iostream>
#include <string>
#include <any>
#include "ConfigTypes.h"
#include "DynamicStore.h"
int main() {
// Ler três entradas
std::string input1; // String que pode ser um número (para testar std::optional)
std::string typeIndicator; // Indicador de tipo: int, double, ou string
std::string value; // Valor correspondente a esse tipo
std::cin >> input1;
std::cin >> typeIndicator;
std::cin >> value;
// TODO: Parte 1 - Testar std::optional com parseValue
// Usar a função parseValue com input1
// Se contiver um valor, imprimir "Parsed: [value]"
// Se estiver vazio, imprimir "Parse failed"
// TODO: Parte 2 - Testar std::variant com Setting
// Criar uma variante Setting baseada em typeIndicator
// Se o tipo for "int", armazenar o valor como inteiro
// Se o tipo for "double", armazenar o valor como double
// Se o tipo for "string", armazenar o valor como string
// Então chamar describeSetting e imprimir o resultado
// TODO: Parte 3 - Testar std::any com DynamicStore
// Criar um DynamicStore
// Armazenar a string "config_loaded" sob a chave "status"
// Recuperá-la e imprimir: "Status: [value]"
// Usar std::any_cast<std::string> para extrair o valor
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner Genérico12Recursos Modernos do C++
Semântica de Movimentação e RvaluesEncaminhamento PerfeitoExpressões Lambda em Profundidadestd::function e std::bindconstexpr e constevalVinculações Estruturadasoptional, variant, anyPratique por conta própria: Compilador de C++ online