std::variant is a type-safe union that can hold values of a type out of a fixed list of types.
Depending on the current alternative inside a variant, it is common to execute dedicated code. There are basically two ways to achieve
that:
  -  Writing code that checks the current alternative, then getting it and running specific code 
-  Letting std::visitperform the check and select the code to run by using overload resolution with the different alternatives
The second option is usually preferable:
  -  It requires less boilerplate code. 
-  It is easy to handle multiple similar alternatives together if desired. 
-  It is usually more robust: if a new alternative is added to the variant, but the visitor does not support it, it will not compile. 
This rule raises an issue when variant::index is called, or when variant::holds_alternative or
variant::get_if is used in a series of if - else if (calling one of these functions in isolation can be an
acceptable lightweight alternative to std::visit in some cases).
Note: When defining the visitor of a variant, it can be nicer to use a series of lambdas by making use of the overloaded pattern
Noncompliant code example
using Variant = std::variant<int, float, string>;
void printType1(Variant const &v) {
    switch(v.index()) { // Noncompliant
        case 0: cout << "int " <<get<int>(v) << "\n"; break;
        case 1: cout << "float " <<get<float>(v) << "\n"; break;
        case 2: cout << "string " <<get<string>(v) << "\n";break;
    }
}
void printType2(Variant const &v) {
    if(auto p = get_if<int>(&v)) { // Noncompliant
        cout << "int " << *p << "\n";
    } else if (auto p = get_if<float>(&v)) {
        cout << "float " << *p << "\n";
    } else if (auto p = get_if<string>(&v)) {
        cout << "string " << *p << "\n";
    }
}
Compliant solution
using Variant = std::variant<int, float, string>;
struct VariantPrinter {
    void operator() (int i) { cout << "int " << i << "\n"; }
    void operator() (float f) { cout << "float " << f << "\n"; }
    void operator() (std::string const &s) { cout << "string " << s << "\n"; }
};
void printType3(Variant const &v) {
    std::visit(VariantPrinter{}, v);
}
// Same principle, but using the overloaded pattern
void printType4(Variant const &v) {
    std::visit(overloaded{
        [](int i){cout << "int " << i << "\n";},
        [](float f){cout << "float " << f << "\n";},
        [](std::string const &s){cout << "string " << s << "\n";}
    }, v);
}