Dynamic Typing with variant and any
Rocket Run's settings screen has a volume (a whole number), a brightness (a decimal) and a nickname (text). You would like one map from setting name to value. But C++ variables have one fixed type. The standard library solves this without giving up safety: the container has a fixed type, and the value inside can vary at run time.
The rule
Known list of types: variant. Truly anything: any. Both check on access.
In a dynamically typed language like Python, a variable can hold an int now and a string later. C++ is static, so it offers two types that carry that flexibility inside them.
std::variant (header <variant>) holds exactly one value from a list you name:
using Setting = std::variant<int, double, std::string>;
Setting volume = 80;
Setting nickname = std::string("Nova");
if (std::holds_alternative<int>(volume)) {
int v = std::get<int>(volume); // 80
}
nickname = 3; // now it holds an intAsking std::get for a type the variant does not currently hold throws std::bad_variant_access. To handle every possible type in one place, std::visit calls your code with whichever value is inside.
std::any (header <any>) holds a single value of any copyable type:
std::any extra = 5;
int n = std::any_cast<int>(extra); // 5
double d = std::any_cast<double>(extra); // throws std::bad_any_castany_cast needs the exact stored type: an int is not quietly converted to a double.
Old C code used void* for this job. It remembers no type at all, so a wrong guess is undefined behaviour rather than a clean exception.
The shortcut
Ask "Can I list every type it might hold?" Yes: std::variant, and the compiler knows the list. No: std::any. If you cannot answer either way, rethink the design.
Choose the container
Volume, brightness and nickname: three known types. Use
std::variant<int, double, std::string>.
Drills
Answer fast — trust the rule.
After std::variant<int, std::string> v = 42; v = std::string("Nova");, what does std::holds_alternative<std::string>(v) return?
What does std::get<int>(v) do when v currently holds a std::string?
A setting is always an int, a double or a string. Which type fits best?
What does std::any a = 5; std::any_cast<double>(a); do?
Remember
- Known list of types:
std::variant. Truly anything:std::any. Both check on access. - Shortcut: "Can I list every type it might hold?"
- Trap: a wrong
getorany_castthrows; check first, and avoidvoid*.
