C++: Cómo funciona el borrado de tipo en std::any

Fuentes: Deriving Type Erasure

Este artículo explora la técnica de "type erasure" (borrado de tipo), un mecanismo fundamental detrás de la implementación de std::any en C++. La idea principal es permitir trabajar con objetos de diferentes tipos a través de una interfaz común, ocultando los tipos concretos subyacentes. El artículo construye std::any desde cero, comenzando con conceptos básicos como polimorfismo con interfaces y templates.

Inicialmente, el polimorfismo se logra a través de clases base y herencia, donde las clases derivadas implementan métodos virtuales definidos en la clase base. Alternativamente, cuando no se puede usar herencia (por ejemplo, con tipos como std::string o int), se pueden usar templates para lograr polimorfismo. Sin embargo, el polimorfismo basado en templates tiene limitaciones: no proporciona un tipo base común en tiempo de ejecución, lo que dificulta el uso con contenedores heterogéneos, y obliga a que los clientes también sean templates, lo que puede aumentar la complejidad del código.

Para superar estas limitaciones, se introduce el concepto de type erasure. Esto implica crear "wrappers" que heredan de la interfaz común (como Shape) y contienen el objeto concreto (como Square o Circle). Estos wrappers implementan los métodos virtuales, simplemente delegando la llamada al objeto interno. El código inicial muestra cómo crear wrappers específicos para cada tipo concreto, lo cual es engorroso. La clave es usar templates para generar estos wrappers automáticamente, reduciendo la cantidad de código repetitivo.

El artículo culmina en la creación de una clase AnyShape que encapsula esta técnica. AnyShape utiliza un std::unique_ptr<Shape> para almacenar el objeto envuelto. Un template constructor permite crear instancias de AnyShape a partir de cualquier tipo que implemente la interfaz Shape (o que pueda ser envuelta por un ShapeWrapper generado por template). La función area() de AnyShape simplemente delega la llamada al método area() del Shape apuntado, ocultando la complejidad del wrapper.

El type erasure permite trabajar con objetos de diferentes tipos de manera uniforme, pero introduce una capa de indirección que puede afectar ligeramente el rendimiento. Además, la implementación presentada utiliza asignación dinámica de memoria (heap allocation), a diferencia de algunas implementaciones de std::any que pueden optimizar el almacenamiento de objetos pequeños en la pila (Small Buffer Optimization - SBO). Finalmente, el artículo destaca la diferencia entre el concepto de "type erasure" (una interfaz con una vtable) y los "concepts" de C++20 (predicados de tiempo de compilación).