std::visit
is usually used in conjunction with Petty C++ - std::variant
to handle data in std::variant
and perform corresponding operations, which was introduced in C++17.
Basic Usage#
std::visit
requires two main parameters:
-
visitor: a callable object (lambda function object) used to handle the data in the variant.
-
variants: one or more
std::variant
instances.
Example#
int main() {
// Create a std::variant that can store an int or std::string
std::variant<int, std::string> var;
// Store an int value in the variant
var = 42;
// Use std::visit to process the value in the variant
std::visit([](auto&& arg) {
std::cout << "Value: " << arg << std::endl;
}, var);
// Store a std::string value in the variant
var = std::string("Hello, world!");
// Use std::visit again to process the new value
std::visit([](auto&& arg) {
std::cout << "Value: " << arg << std::endl;
}, var);
return 0;
}
Multiple Variants#
When handling multiple variants, the visitor needs to be able to handle all possible types. If different type combinations are not branched in the visitor, the visitor will generate different function call logic for each possible type combination. If there are multiple variants, each variant contains multiple types, and the possible type combinations will grow exponentially.
To avoid this situation, the following methods can be used to save overhead:
- Reduce the number of types in the variant.
- Use
std::monostate
// todo - Use branching.
- Use polymorphism.
template<typename T>
void process(const T& value) {
if constexpr (std::is_same_v<T, int>) {
std::cout << "Processing int: " << value << std::endl;
} else if constexpr (std::is_same_v<T, std::string>) {
std::cout << "Processing string: " << value << std::endl;
} else {
std::cout << "Processing other type" << std::endl;
}
}
By using if constexpr and template type deduction, the compiler will only generate specific logic for common types, while the generic processing reduces unnecessary code generation.