C++: Optimiza el polimorfismo con desvirtualización

Fuentes: Devirtualization and Static Polymorphism

Este artículo explora el tema de la 'desvirtualización' y el 'polimorfismo estático' en C++, abordando un problema común de rendimiento en diseños polimórficos. La programación polimórfica, a través de funciones virtuales, permite que el código se adapte a diferentes tipos en tiempo de ejecución. Sin embargo, esta flexibilidad tiene un costo: la 'despacho virtual'. Cada objeto de clase base contiene un puntero a una 'tabla virtual' (vtable), y cada llamada a una función virtual implica una indirección a través de esta tabla, lo que introduce sobrecarga en términos de tamaño del objeto, tiempo de ejecución y dificulta la optimización del compilador (como el 'inlining').

Los compiladores intentan 'desvirtualizar' estas llamadas, es decir, determinar en tiempo de compilación a qué implementación específica de la función se debe llamar, eliminando así la indirección de la vtable. Esto es posible cuando el tipo concreto de un objeto es conocido en tiempo de compilación. El artículo explica cómo las opciones del compilador como -fwhole-program y -flto (Optimización en Tiempo de Enlace) facilitan esta desvirtualización al permitir al compilador realizar optimizaciones a través de múltiples archivos de código.

Cuando la desvirtualización no es posible, el artículo introduce el 'polimorfismo estático' como alternativa. Una técnica común es el 'Patrón de Plantilla Recurrente Curiosamente' (CRTP), donde la clase base se instancia con la clase derivada como argumento de plantilla. Esto permite al código acceder a miembros de la clase derivada directamente a través de static_cast, evitando el uso de funciones virtuales. Una característica más reciente de C++23, deducing this, simplifica este enfoque al permitir la creación de funciones con acceso a la clase derivada sin tener que templar toda la clase.

Finalmente, el artículo destaca las consideraciones importantes: la desvirtualización y el polimorfismo estático pueden limitar la capacidad de 'upcasting' (convertir a un tipo base), y requieren que cualquier funcionalidad compartida entre diferentes tipos derivados también se implemente con plantillas. La elección entre despacho virtual, desvirtualización y polimorfismo estático implica un equilibrio entre flexibilidad y rendimiento, y depende del contexto específico del problema.