Menu
CoddyTech

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 entre 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;  // Sem valor
}
int main() {
    auto result = findIndex("hello", 'l');
    if (result.has_value()) {
        std::cout << "Found at: " << *result << "\n";  // 2
    }
}

std::variant<Types...> é uma união type-safe 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 armazenar 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 que podem ser nulos, std::variant quando você souber os tipos possíveis em tempo de compilação e std::any somente quando realmente precisar de flexibilidade de tipos em tempo de execução.

challenge icon

Desafio

Fácil

Vamos criar um sistema de configuração que demonstre o poder dos modernos tipos vocabulários do C++. Você criará um gerenciador de configurações flexível que lida com valores que podem estar ausentes, podem conter diferentes tipos ou precisam 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 parseValue que receba uma string e tente analisá-la como um inteiro. Se a string representar um inteiro válido, retorne o inteiro encapsulado em std::optional. Se a análise falhar (a string contiver caracteres não numéricos), retorne std::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 Setting usando std::variant que possa conter um int, um double ou uma std::string.

    Por fim, crie uma função chamada describeSetting que receba um Setting e retorne uma string descrevendo o tipo que ele contém e seu valor neste formato:

    • Para int: Integer: [value]
    • Para double: Double: [value]
    • Para string: String: [value]
  • DynamicStore.h: Crie um armazenamento simples de chave-valor usando std::any.

    Defina uma classe DynamicStore que possa armazenar valores de qualquer tipo. Ela deve ter:

    • Um método set que receba uma chave string e um valor std::any, armazenando-os internamente (use um std::map)
    • Um método get que receba uma chave e retorne o valor std::any (retorne um std::any vazio se a chave não existir)
    • Um método hasKey que retorne true se a chave existir
  • main.cpp: Leia três entradas:
    1. Uma string que pode ser um número (para testar std::optional)
    2. Um indicador de tipo: int, double ou string
    3. Um valor correspondente a esse tipo

    Demonstre os três tipos vocabulários:

    Primeiro, use sua função parseValue com a primeira entrada. Se ela contiver um valor, imprima Parsed: [value]. Se estiver vazia, imprima Parse failed.

    Segundo, crie uma variante Setting com base no indicador de tipo. Se o tipo for int, armazene o valor como um inteiro. Se for double, armazene-o como um double. Se for string, armazene-o como uma string. Em seguida, chame describeSetting e imprima o resultado.

    Terceiro, crie um DynamicStore, armazene a string "config_loaded" sob a chave "status", depois recupere-a e imprima: Status: [value]. Use std::any_cast<std::string> para extrair o valor.

Por exemplo, com as entradas 42, int e 100:

Parsed: 42
Integer: 100
Status: config_loaded

Com as entradas hello, double e 3.14:

Parse failed
Double: 3.14
Status: config_loaded

Com as entradas -15, string e username:

Parsed: -15
String: username
Status: config_loaded

Lembre-se de incluir os headers 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() {
    // Lê 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 - Teste std::optional com parseValue
    // Use a função parseValue com input1
    // Se contiver um valor, imprima "Parsed: [value]"
    // If it's empty, print "Parse failed"
    
    
    // TODO: Parte 2 - Teste std::variant com Setting
    // Crie uma variante Setting com base em typeIndicator
    // Se o tipo for "int", armazene value como inteiro
    // Se o tipo for "double", armazene value como double
    // Se o tipo for "string", armazene value como string
    // Então chame describeSetting e imprima o resultado
    
    
    // TODO: Parte 3 - Teste std::any com DynamicStore
    // Crie um DynamicStore
    // Armazene a string "config_loaded" sob a chave "status"
    // Recupere-o e imprima: "Status: [value]"
    // Use std::any_cast<std::string> para extrair o valor
    
    
    return 0;
}
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de C++ online