You can use variants just fine without a visitor. Like,
std::variant<string, int, bool> v;
...
if (auto pstr = std::get_if<string>(&v)) {
cout << *pstr << endl;
}
else if (auto pint = std::get_if<int>(&v)) {
cout << *pint << endl;
}
else if (auto pbool = std::get_if<bool>(&v)) {
cout << *pbool << endl;
}
else {
count << "null" << endl;
}
which is not much worse than the pattern matching syntax.