Última actualización:2026-10-12
Polimorfismo en C++: funciones virtuales, CRTP y variant¶
Prueba de rendimiento del polimorfismo en C++: CRTP vs std::variant vs virtual¶
El polimorfismo es un concepto importante de la programación orientada a objetos: permite que una única interfaz represente distintos tipos. C++ ofrece varias formas de implementar el polimorfismo; este artículo tratará el polimorfismo en tres escenarios:
- Funciones virtuales: la forma tradicional de implementar el polimorfismo en C++ es usar funciones virtuales, con clases base y derivadas que aportan implementaciones concretas.
- std::variant: introducido en C++17, std::variant implementa un polimorfismo sin necesidad de herencia.
- CRTP (Curiously Recurring Template Pattern): el CRTP es una técnica bastante peculiar que logra el polimorfismo mediante el extraño patrón recurrente de las plantillas.
Las combinaciones de escenarios probadas son las siguientes:
- Solo crtp
- crtp + std::variant
- virtual
- std::variant + std::visit
- std::variant + std::get_if
- std::variant + std::holds_alternative
Compiladores utilizados:
- gcc 13.2
- clang17.0
El código completo de la prueba ya está en el Planeta; aquí pego el código clave
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0;
};
class Circle : public Shape
class Rectangle : public Shape
// virtual
static void BM_VirtualFunction(benchmark::State& state) {
std::vector<std::unique_ptr<Shape>> shapes;
shapes.reserve(1000);
for (int i = 0; i < 1000; ++i) {
if (i % 2 == 0) {
shapes.emplace_back(std::make_unique<Circle>(5.0));
} else {
shapes.emplace_back(std::make_unique<Rectangle>(4.0, 6.0));
}
}
for (auto _ : state) {
double totalArea = 0.0;
for (const auto& shape : shapes) {
totalArea += shape->area();
}
benchmark::DoNotOptimize(totalArea);
}
}
BENCHMARK(BM_VirtualFunction);
static void BM_VariantVisit(benchmark::State& state) {
std::vector<std::variant<Circle, Rectangle>> shapes;
shapes.reserve(1000);
for (int i = 0; i < 1000; ++i) {
if (i % 2 == 0) {
shapes.emplace_back(Circle(5.0));
} else {
shapes.emplace_back(Rectangle(4.0, 6.0));
}
}
for (auto _ : state) {
double totalArea = 0.0;
for (const auto& s : shapes) {
totalArea += std::visit([](const auto& shape) { return shape.area(); }, s);
}
benchmark::DoNotOptimize(totalArea);
}
}
static void BM_CRTPVariant(benchmark::State& state) {
std::vector<ShapeVariant> shapes;
shapes.reserve(1000);
for (int i = 0; i < 1000; ++i) {
if (i % 2 == 0) {
shapes.emplace_back(crtp_light::Circle(5.0));
} else {
shapes.emplace_back(crtp_light::Rectangle(4.0, 6.0));
}
}
for (auto _ : state) {
double totalArea = 0.0;
for (const auto& s : shapes) {
totalArea += std::visit([](const auto& shape) { return shape.area(); }, s);
}
benchmark::DoNotOptimize(totalArea);
}
}
BENCHMARK(BM_CRTPVariant);
// ...
Resultado de la prueba 1: compilando con gcc, puede verse que virtual y std::variant apenas difieren en rendimiento, pero la diferencia con crtp es enorme.

Resultado de la prueba 2: compilando con clang, la tendencia general es similar a la de gcc; solo crtp + std::variant sufre un retroceso de rendimiento notable, posiblemente también debido al uso de std::visit aquí.

En el libro A Tour of C++ se menciona:
This is basically equivalent to a virtual function call, but potentially faster. As with all claims of performance, this ''potentially faster'' should be verified by measurements when performance is critical. For most uses, the difference in performance is insignificant.
Coincide básicamente con esta prueba.
Temas relacionados¶
- Prueba de rendimiento del SSO de string en C++ | C++那些事
- Comparación de rendimiento: memcpy frente a std::copy | C++那些事