Variant

Let us get started with a minimal variant interface. This will give us a frame
of reference throughout the post. We only need the following:

v.index(): Returns the index of the currently stored value.

get<I>(v): Returns the currently stored value if v.index() == I else
throws std::bad_variant_access.

Visitation

Here is the signature of visit:

template<typenameF,typename...Vs>decltype(auto)visit(F&&f,Vs&&...vs);

It invokes f with the currently stored value of each of the vs....

Consider the following function:

// `F` and `Vs...` is in context from outer scope.
template<std::size_t...Is>constexprdecltype(auto)dispatch(Ff,Vs...vs){static_assert(sizeof...(Is)==sizeof...(Vs));std::invoke(static_cast<F>(f),get<Is>(static_cast<Vs>(vs))...);}

Since get<I>(v) returns the currently stored value of v for us,
we ultimately want to invoke dispatch<Is...>(f, vs...) where Is... are
the compile-time values of vs.index()....

How do we do that?

N-dimensional Matrix of Function Pointers

The approach is to build an N-dimensional matrix of function pointers
(representative of the N-ary cartesian product), then use vs.index()... to
index into the matrix at runtime where the appropriate &dispatch<Is...> will
be waiting for us.

For example, given variant<A, B> we have the following function matrix:

A

B

&dispatch<0>

&dispatch<1>

given variant<A, B> and variant<X, Y, Z>, we have:

X

Y

Z

A

&dispatch<0, 0>

&dispatch<0, 1>

&dispatch<0, 2>

B

&dispatch<1, 0>

&dispatch<1, 1>

&dispatch<2, 2>

Implementation

make_fmatrix

The base case should look familiar. Given F, Vs... and Is..., it returns
the function pointer &dispatch<Is...> from above.

The recursive case implements the N-ary cartesian product resulting in the
N-dimensional matrix. We build this recursively, keeping the first
std::index_sequence as our accumulator. For each given list, we make a
recursive call with each of its elements appended onto the accumulator, and the
rest of the lists.

We’re now left with the task of accessing the fmatrix with vs.index()....
Since we can’t say something like: fmatrix([vs.index()]...) to mean
fmatrix[i_0][i_1]...[i_N], we need to introduce a helper function.

We’ve managed to decouple the implementation of variant and the visitation
mechanism, generalized to any N. This means that we can implement parts of
variant that required single visitation using visit. For example, copy
construction could look something along the lines of:

NOTE: It’s actually a little more complicated than this because duplicate
types in a variant have distinct states. We therefore need to initialize the
alternative in this at the same index as the one stored in that.