Have a look at std::any : https://en.cppreference.com/w/cpp/utility/any/
you can place a value of any type T in it (well, obviously it needs to be constructible etc.), or a std::nullopt, which is like an "Empty" or "Nothing" indicator that is not in T.
Then you can pass the `any` around without knowing its type. Finally, when you want to restore the typed value, you use any::get<T>(). It will succeed if T is the correct type, and throw an exception otherwise.
This was introduced into C++ in the C++17 version of the standard. Before, it existed as a Boost library facility.