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::visit
perform 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);
}