visit benchmark results

C++
Author

dev::author

Published

August 25, 2026

Introduction

A month ago I published a custom implementation of std::visit that stores function pointers in a flat one-dimensional array. If you haven’t read it, start there - this post builds directly on top of it.

visit implementation using flat arrays

For completeness, this is the implementation of visit using a one-dimensional array.

visit implementation using multi-dimensional arrays

A multidimensional array is an array of T elements, where the type T is a generic type - either a scalar or an array itself. Hence, a multidimensional array can be easily coded up as follows:

namespace qc{
    /**
     * @brief A light n-dimensional array.
     */
    template<typename T, std::size_t Size>
    struct poly_array{
        T m_buffer[Size] = {};

        constexpr T& operator[](std::size_t idx){
            return m_buffer[idx];
        }

        // Base case
        decltype(auto) constexpr at(std::size_t index){
            return m_buffer[index];
        }

        // Case with a pack of indices
        template<typename... Indices>
        decltype(auto) constexpr at(std::size_t index, Indices... indices){
            return m_buffer[index].at(indices...);
        }
    };
}

The .at(std::size_t, Indices...) method accepts a variadic pack of indices. As a concrete example, if I define a multi-dimensional array a of dimensions \(2 \times 3 \times 4\), a is a collection of two \(3 \times 4\) matrices. So, a.at(0,1,2) is (1,2)-th element in the 0-th matrix. Hence, we should recursively invoke a[0].at(1,2).

Next, we write a helper struct called dispatcher. The dispatcher has a static constexpr method called dispatch that invokes any callable on a pack of variants. It’s basically a smart wrapper over std::invoke, that checks the return type of the callable at compile-time and dispatches to an appropriate branch.

template<size_t... Indices>
struct dispatcher{
    template<typename Func, typename... Vs>
    static constexpr auto dispatch(Func&& f, Vs&&... vs){
        using return_type_t = decltype(std::forward<Func>(f)(std::get<0>(std::forward<Vs>(vs))...));
        if constexpr(std::is_same_v<return_type_t, void>){
            std::invoke(std::forward<Func>(f), std::get<Indices>(std::forward<Vs>(vs))...);
        }else{
            return std::invoke(std::forward<Func>(f), std::get<Indices>(std::forward<Vs>(vs))...);
        }
    };
};

Here is the complete code-listing.

Insights