std::any
é um recurso da biblioteca padrão C++ que foi introduzido no C++17.
Este componente pertence ao conjunto de classes de contêineres com segurança de tipo, fornecendo um meio seguro para armazenar e manipular valores de qualquer tipo.
Ele é especialmente útil quando você precisa lidar com situações em que o tipo da variável pode(pleonasmo): variar! 😃
Aí você diz:
— Ah, cara! De boa. Para esses casos eu uso o
void *
.
Sim, realmente você tem razão, mas você já viu como a nova geração está em relação a segurança de memória ???
Lembrando que o termo segurança é usado em Português, pois não existe uma palavra que se adeque a tradução para
Safe
, ou seja:Safe
!=Seguro
)! 😛
Sem dizer que void *
é realmente perigoso!
Se você fizer isso, funciona:
Mas, a chance de isso dar mer%$a
é grande! Ao final do uso dessas variáveis, some_data
vai continuar existindo, ou seja, tempo de vida indefinido!
E é para subsitituir o void*
que o std::any
foi criado no C++ Moderno que, com certeza, é totalmente Safe!
Em outras palavras, ele é um wrapper que encapsula sua variável para um shared_ptr
(ponteiros inteligentes) da vida! Sim, e existe até um std::make_any
!!!
std::any
Primeiramente você precisa incluir o cabeçalho dele:
Logicamente, só funciona a partir do C++17 como foi dito no início!
E agora o mesmo código que foi apresentado acima, mas usando std::any
:
No código acima vimos que:
std::any some_data;
- Declara a variável;std::any_cast<T>(some_data)
- Converte para o tipo desejado;std::make_any<T>
- Outra forma de criar objetos;some_data.type().name()
- Obtém o tipo de dado sem precisar de typeinfo
.E você pode usar pra absolutamente tudo: std::vector
, Lambda e tudo que existir de tipo de dado!
E o cara pergunta outra coisa:
— Tá! E se eu quiser acabar o tempo de vida do
std::any
manualmente?
Basta usar a estrutura de união reset
ou até mesmo com o operador de incialização:
— E pra verificar se
std::any
está vazio? Usehas_value()
:
O type()
sem união com name()
pode ser usado para comparar tipos:
Para usar os nomes booleanos use:
std::cout << std::boolalpha << (some_data.type() == typeid(int)) << '\n';
.
Para lançar exceções você deve usar o std::bad_any_cast
:
Para verificar se realmente tudo está entre os conformes, nunca se esqueça de usar as flags para seu compilador: -Wall -Wextra -pedantic -g -fsanitize=address
.
Imagine você ter um código que precisa concatenar vários tipos e retornar uma string. No entanto, um dos tipos pode ser: int, double ou std::string.
Se usar std::any_cast<T>
no retorno assim:
Exemplo:
Compile:
g++ -Wall -Wextra -pedantic -g -fsanitize=address main.cpp
.
Terá um std::bad_any_cast
na saída:
Ambas conversões(da msg
e da object
) estão incorretas:
Você precisa fazer um switch case
para o enumerador e no caso do parâmetro object
: Precisará usar o has_value()
, armazenar o type()
em std::type_info&
e usar um std::stringstream
para atribuir o tipos de retorno com união ao: str()
, assim:
Que função!!! :O , mas assim seu código ficará safe! Compile: g++ -Wall -Wextra -pedantic -g -fsanitize=address main.cpp
e após rodar ./a.out
, a saída será o esperado:
Parece trabalhoso, mas essa é a forma correta de você finalizar o tempo de vida de um tipo qualquer!
Além de totalmente SAFE, o std::any
é muito prático e uma mão na roda!
Em alguns casos também é interessante usar std::variant e obter qual o tipo com .index()
, ex.:
//...
std::variant <int, std::string, double> var;
//...
if(var.index() == 1){
std::cout << "var é uma std::string\n";
break;
//...
Escolha std::any
para flexibilidade máxima e quando os tipos armazenados são desconhecidos até o tempo de execução. Escolha std::variant
para maior segurança e desempenho quando você sabe todos os tipos possíveis em tempo de compilação.
Para mais informações acesse: https://en.cppreference.com/w/cpp/utility/any