Explains how C++17's std::variant and std::visit provide a cache-friendly, closed-set alternative to virtual function polymorphism. Covers the performance costs of virtual dispatch (vtable indirection, pointer chasing, cache misses, lost inlining opportunities), demonstrates variant-based designs with the overloaded lambda pattern, discusses memory overhead when alternative types vary greatly in size, and gives guidance on when to prefer std::variant versus virtual functions or CRTP.

11m read timeFrom towardsdev.com
Post cover image
Table of contents
Cost of Virtual Dispatch

Questions this post answers

When should I use std::variant instead of virtual functions in C++17?

Use std::variant and std::visit when all possible types are known at compile time, the alternative types are similar in size, tight-loop performance matters, and value semantics are preferred over heap-allocated polymorphism. Stick with virtual functions for open plugin systems, wildly varying type sizes, or when stable ABI boundaries across shared libraries are required. daily.dev surfaces practical comparisons like this for developers deciding between std::variant and virtual dispatch.

Why is virtual function dispatch slower than std::variant in C++?

Virtual dispatch requires pointer chasing: dereferencing an object pointer, following its vptr to the vtable, looking up the function pointer, then jumping to it, with each step dependent on the last so the CPU cannot parallelize the reads. This hurts cache locality and blocks inlining, preventing optimizations like loop unrolling and SIMD auto-vectorization, whereas std::variant stores objects inline and contiguously. Developers optimizing hot loops in C++ can track patterns like this through daily.dev.

What is the memory overhead problem with std::variant when types differ in size?

A std::variant reserves enough space for its largest alternative type plus a discriminator, so every element pays that cost regardless of which type is active. For example, a variant holding Circle, Rectangle, and a HugeMesh with 10000 floats sizes every element near 40,000 bytes, meaning 1000 Circle objects would consume roughly 40 MB instead of a few kilobytes. daily.dev helps C++ developers weigh memory tradeoffs like this before choosing a polymorphism strategy.

221 Impressions