any es una de las características más nuevas de C++17 que proporciona un contenedor seguro para almacenar valores únicos de cualquier tipo. En términos sencillos, es un contenedor que permite almacenar cualquier valor en él sin preocuparse por la seguridad del tipo . Actúa como una extensión de C++ al imitar un comportamiento similar a un tipo de objeto en .NET/Java o un tipo void* en lenguaje C. Ha sido diseñado en base a boost::any y está disponible en el archivo de encabezado «cualquiera» .
Sintaxis:
any var= value/object;
donde el valor es algo así como «17» o «Hola Mundo»
Inicialización de cualquier:
any se puede construir de tres maneras diferentes usando:
- Copiar inicialización
Sintaxis:
any variable_name = object/value;
- Constructor parametrizado / inicializador de llaves.
Sintaxis:
any variable_name ( object/value);
- Usando el operador de asignación
Sintaxis:
any variable_name; variable_name= object/value;
- emplace : Cambia el objeto contenido, construyendo el nuevo objeto directamente
- reset : Destruye el objeto contenido (Llama al destructor del objeto)
- has_value : comprueba si «cualquier_variable» tiene un valor dentro de ella
- type : Devuelve el id de tipo del valor contenido
Convirtiendo el valor any_var en su tipo original:
Se debe usar any_cast<type>( any_var ) para convertir el valor de any_var en su tipo original . Si un valor almacenado tiene un tipo diferente al que intenta convertir, el compilador lanzará una excepción » bad_any_cast «.
Nota: El tipo durante el lanzamiento debe ser exactamente el mismo que el tipo original. No hay promoción o degradación automática durante el reparto. Por lo tanto, se debe tener especial cuidado al convertir el valor a su tipo original.
Un ejemplo simple (ilustra la construcción/valores de lectura de cualquiera)
#include <any> #include <iostream> #include <string> using namespace std; int main() { try { // Integer 42: Using the copy initialisation any value = 42; cout << "\n Value: " << any_cast< int >(value); // Using the assignment operator // to store a string value = "Hello World" ; cout << "\n Value: " << any_cast< const char *>(value); // Using the parametrized constructor any val(19.0); cout << " \n Value: " << any_cast< double >(val); any val_brace{ string( "Brace Initialisation" ) }; cout << " \n Value: " << any_cast<string>(val_brace); } catch (bad_any_cast& e) { cout << "\n" << e.what(); } return 0; } |
Producción:
Value: 42 Value: Hello World Value: 19 Value: Brace Initialisation
Funciones de los miembros:
Veamos los métodos uno por uno en detalle:
- emplace: la función de miembro emplace es similar al operador de asignación y se usa para cambiar el objeto contenido con un nuevo objeto.
Programa:
// C++ program to demonstrate
// emplace() method of any class
#include <any>
#include <iostream>
#include <string>
int
main()
{
try
{
any value = 4.2;
cout <<
" \n Value: "
<< any_cast<
double
>(value);
value.emplace<
int
>(44);
cout <<
" \n Value: "
<< any_cast<
int
>(value);
}
catch
(bad_any_cast& e) {
cout <<
"\n"
<< e.what();
}
return
0;
}
Producción:
Value: 4.2 Value: 44
- reset: Destruye el objeto contenido llamando al destructor del objeto contenido.
Programa:
// C++ program to demonstrate
// reset() method of any class
#include <any>
#include <iostream>
#include <string>
int
main()
{
try
{
any var = 4.2;
cout <<
" \n Value: "
<< any_cast<
double
>(var);
var.reset();
if
(!var.has_value())
cout <<
" \n No value found in var variable"
;
}
catch
(bad_any_cast& e) {
cout <<
"\n"
<< e.what();
}
return
0;
}
Producción:
Value: 4.2 No value found in var variable
- has_value: esta función miembro se utiliza para verificar si el objeto contiene un valor o no
Programa:
// C++ program to demonstrate
// has_value() method of any class
#include <any>
#include <iostream>
#include <string>
int
main()
{
try
{
any var = 9.5;
cout <<
" \n Value: "
<< any_cast<
double
>(var);
if
(var.has_value())
cout <<
" \n Value found of type "
<< var.type().name();
}
catch
(bad_any_cast& e) {
cout <<
"\n"
<< e.what();
}
return
0;
}
Producción:
Value: 9.5 Value found of type d
- type: esta función miembro devuelve una estructura type_info que se puede usar para obtener las propiedades del objeto almacenado.
Programa:
// C++ program to demonstrate
// type() method of any class
#include <any>
#include <iostream>
#include <string>
int
main()
{
try
{
any var = 12.0f;
cout <<
" \n Type: "
<< var.type().name();
var =
"Hello World"
;
cout <<
" \n Type: "
<< var.type().name();
}
catch
(bad_any_cast& e) {
cout <<
"\n"
<< e.what();
}
return
0;
}
Producción:
Type: f Type: PKc
Usos de cualquier
Los usos típicos incluyen
- En Bibliotecas, cuando un tipo de biblioteca tiene que contener o pasar algo sin conocer el conjunto de tipos disponibles.
- Paso de mensajes
- Implementación de bibliotecas de análisis para ej. analizador JSON
- Interfaz de usuario: los controles pueden contener cualquier cosa
- Sistema de componentes de entidad.
Uno de los principales beneficios de cualquiera es su sustitución viable con void*. void* tiene una capacidad limitada (solo almacena tipos de punteros) y se considera un patrón inseguro.
Manejo de errores:
Hay dos opciones con respecto al manejo de errores para cualquier clase:
- Uso de excepciones: bad_any_cast es la excepción lanzada por las formas de retorno de valor de any_cast en caso de discrepancia de tipo.
Ejemplo:
// C++ program to demonstrate
// using exceptions of any class
#include <any>
#include <iostream>
#include <string>
int
main()
{
try
{
any var = 12.0f;
cout <<
" \n Value: "
<< any_cast<
double
>(var);
}
catch
(bad_any_cast& e) {
cout <<
"\n"
<< e.what();
}
return
0;
}
Producción
Value: bad any_cast
- Devolver un puntero: devolver un puntero es útil cuando el mecanismo de excepción se ha deshabilitado en el compilador. Esta sobrecarga particular de any_cast devuelve el puntero al objeto contenido si la conversión fue exitosa y devuelve nullptr .
Ejemplo:
// C++ program to demonstrate
// returning pointer of any class
#include <any>
#include <iostream>
#include <string>
using
namespace
std;
int
main()
{
any var = 12.0f;
// Special Overload of any_cast
auto
* tval = any_cast<
float
>(&var);
if
(!tval) {
// Type-mismatch
cout <<
" \n Bad_any_cast "
;
}
else
{
// Value of the object
cout <<
" \n Value: "
<< *tval;
}
return
0;
}
Producción:
Value: 12
Consideraciones de memoria
Aunque cualquiera da mucha flexibilidad con el idioma, el problema principal con cualquiera es la asignación de memoria dinámica adicional. Como el contenedor no es consciente del objeto que contiene, la asignación dinámica se convierte en una necesidad para cualquiera.
Pero de acuerdo con el estándar, las implementaciones deben evitar el uso de memoria asignada dinámicamente para un valor contenido pequeño. Ejemplo: donde el objeto construido contiene solo un int. Esta optimización de objetos pequeños solo se aplicará a los tipos T para los que is_nothrow_move_construtible_v verdadero.
Lo que esto normalmente significa es que el compilador debe usar la optimización de búfer pequeño ( SBO ) en la que se debe reservar una cierta cantidad de memoria para el tipo que se va a contener.
En la tabla anterior, se puede ver que la memoria reservada para SBO en algunos casos podría llegar hasta los 64 bytes. (MSVC-64 bits). Esto significa que cada objeto de cualquiera requeriría 64 bytes de memoria reservados incluso si el objeto es pequeño, lo que representa una sobrecarga de memoria considerable.
Aunque any es una característica muy poderosa en C++. Viene con una cantidad considerable de sobrecarga en términos de memoria .
Publicación traducida automáticamente
Artículo escrito por Gopi_Krishna_Menon y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA