Последнее обновление:2026-10-12
Полиморфизм в C++: виртуальные функции, CRTP и variant¶
Тест производительности полиморфизма в C++: CRTP vs std::variant vs virtual¶
Полиморфизм — важное понятие объектно-ориентированного программирования, позволяющее одному интерфейсу представлять разные типы. C++ предоставляет несколько способов реализовать полиморфизм; в этой статье будут рассмотрены три сценария полиморфизма:
- Виртуальные функции: традиционный способ реализации полиморфизма в C++ — использование виртуальных функций. Он предполагает применение базового и производных классов для конкретных реализаций.
- std::variant: появившийся в C++17 std::variant реализует полиморфизм без наследования.
- CRTP (Curiously Recurring Template Pattern): CRTP — довольно специфическая техника, реализующая полиморфизм через «любопытно рекуррентный» шаблонный паттерн.
Тестировались следующие комбинации сценариев:
- чистый crtp
- crtp + std::variant
- virtual
- std::variant + std::visit
- std::variant + std::get_if
- std::variant + std::holds_alternative
Использовавшиеся компиляторы:
- gcc 13.2
- clang17.0
Полный тестовый код размещён в сообществе (知识星球), здесь приведу ключевой код
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);
// ...
Результат теста 1: компиляция gcc — видно, что производительность virtual и std::variant почти не различается, но с crtp разница очень велика.

Результат теста 2: компиляция clang — общая тенденция похожа на gcc, и лишь у crtp + std::variant наблюдается заметный откат производительности, что, возможно, также связано с использованием здесь std::visit.

В книге A Tour of C++ говорится:
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.
Что в целом совпадает с результатами данного теста.
Похожие темы¶
- Тест производительности SSO в C++ string | C++那些事
- Сравнение производительности memcpy и std::copy | C++那些事