๐ŸŽ“ A1Learner
๐Ÿ”
0 of 28 lessons completed

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?