C++ Variant and Visit
A variant holds one value from a fixed list of types. The held type can change when a new value from the list is assigned.
One box, two types
The variant starts with an int. It is then assigned a string, and index() moves from 0 to 1.
#include <iostream>
#include <string>
#include <variant>
int main() {
std::variant<int, std::string> value = 42;
std::cout << std::get<int>(value) << " index " << value.index() << "\n";
value = std::string("hello");
std::cout << std::get<std::string>(value) << " index " << value.index() << "\n";
return 0;
}Asking which type is held
holds_alternative answers before each read. The variant holds an int first and a string after reassignment.
#include <iostream>
#include <string>
#include <variant>
int main() {
std::variant<int, std::string> value = 7;
std::cout << std::boolalpha;
std::cout << std::holds_alternative<int>(value) << "\n";
if (std::holds_alternative<int>(value)) {
std::cout << std::get<int>(value) << "\n";
}
value = std::string("pen");
std::cout << std::holds_alternative<int>(value) << "\n";
if (std::holds_alternative<std::string>(value)) {
std::cout << std::get<std::string>(value) << "\n";
}
return 0;
}A pointer that may be null
The get_if call asks for an int pointer. A match gives a pointer to the value, a mismatch gives nullptr, and the star reads the value behind a matching pointer.
#include <iostream>
#include <string>
#include <variant>
int main() {
std::variant<int, std::string> number = 42;
std::variant<int, std::string> word = std::string("hello");
int* first = std::get_if<int>(&number);
if (first != nullptr) {
std::cout << *first << "\n";
} else {
std::cout << "Not a number" << "\n";
}
int* second = std::get_if<int>(&word);
if (second != nullptr) {
std::cout << *second << "\n";
} else {
std::cout << "Not a number" << "\n";
}
return 0;
}Visiting both cases
The Printer visitor has one overload for int and one for string. std::visit picks the overload that matches the held value.
#include <iostream>
#include <string>
#include <variant>
struct Printer {
void operator()(int value) const {
std::cout << "Number: " << value << "\n";
}
void operator()(const std::string& value) const {
std::cout << "Text: " << value << "\n";
}
};
int main() {
std::variant<int, std::string> value = 42;
std::visit(Printer{}, value);
value = std::string("hello");
std::visit(Printer{}, value);
return 0;
}Example
#include <iostream>
#include <string>
#include <variant>
#include <vector>
struct Reporter {
void operator()(int value) const {
std::cout << "Code: " << value << "\n";
}
void operator()(const std::string& value) const {
std::cout << "Message: " << value << "\n";
}
};
int main() {
std::vector<std::variant<int, std::string>> messages;
messages.push_back(200);
messages.push_back(std::string("Saved"));
messages.push_back(404);
for (const auto& message : messages) {
std::visit(Reporter{}, message);
}
return 0;
}Key points
- The <variant> header gives a program the std::variant type.
- std::variant<int, std::string> holds either an int or a string at one time.
- index() gives the position of the current type in the list, starting at 0.
- std::get<T> reads the held value when the held type matches.
- std::holds_alternative<T> tells whether the variant currently holds that type.
- std::get_if<T> gives a pointer to the held value, or nullptr when the type differs.
- std::visit calls the matching overload of a visitor for the held value.
In this example, a vector holds three messages, and each message is a variant of an int code or a string. std::visit sends each message to the matching Reporter overload. The codes and the text print in the order the messages were stored.
Note
A variant holds exactly one of its listed types at a time, and it always knows which one.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A std::variant<int, std::string> holds a string. What does index() return?