std:: visit

Applies the visitor vis to the variants vars

Effectively returns

std:: invoke ( std:: forward < Visitor > ( vis ) , std :: get < is > ( std:: forward < Variants > ( vars ) ) ... )

, where is... is vars.index()... .

The call is ill-formed if the invocation above is not a valid expression of the same type and value category, for all combinations of alternative types of all variants.

Return value

The value returned by the selected invocation of the visitor, converted to the common type of all possible std::invoke expressions.

Throws std::bad_variant_access if any variant in vars is valueless_by_exception .

When the number of variants is zero or one, the invocation of the callable object is implemented in constant time, i.e. it does not depend on sizeof...(Types) .

If the number of variants is larger than 1, the invocation of the callable object has no complexity requirements.

cppreference.com

Std:: visit.

Applies the visitor vis ( Callable that can be called with any combination of types from variants) to the variants vars .

These overloads participate in overload resolution only if every type in std:: remove_reference_t < Variants > ... is a (possibly const-qualified) specialization of std::variant , or a (possibly const-qualified) class C such that there is exactly one std::variant specialization that is a base class of C and it is a public and unambiguous base class.

Effectively returns

std:: invoke ( std:: forward < Visitor > ( vis ) ,             std:: get < is > ( std:: forward < VariantBases > ( vars ) ) ... )

, where every type in VariantBases... is the unique std::variant specialization determined above, except that const , & , or && is added to it if the corresponding argument is of a const-qualified type, is an lvalue, or is an rvalue, respectively, and is... is std:: forward < VariantBases > ( vars ) . index ( ) ... .

[ edit ] Parameters

[ edit ] return value, [ edit ] exceptions.

Throws std::bad_variant_access if any variant in vars is valueless_by_exception . Whether any variant is valueless by exception is determined as if by ( std:: forward < VariantBases > ( vars ) . valueless_by_exception ( ) || ... ) .

[ edit ] Complexity

When the number of variants is zero or one, the invocation of the callable object is implemented in constant time, i.e. it does not depend on sizeof...(Types) .

If the number of variants is larger than 1, the invocation of the callable object has no complexity requirements.

[ edit ] Notes

Let n be ( 1 * ... * std:: variant_size_v < std:: remove_reference_t < VariantBases >> ) , implementations usually generate a table equivalent to an (possibly multidimensional) array of n function pointers for every specialization of std::visit , which is similar to the implementation of virtual functions .

Implementations may also generate a switch statement with n branches for std::visit (e.g. the MSVC STL implementation uses a switch statement when n is not greater than 256).

On typical implementations, the time complexity of the invocation of vis can be considered equal to that of access to an element in an (possibly multidimensional) array or execution of a switch statement.

[ edit ] Example

[ edit ] defect reports.

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

[ edit ] See also

  • Recent changes
  • Offline version
  • What links here
  • Related changes
  • Upload file
  • Special pages
  • Printable version
  • Permanent link
  • Page information
  • In other languages
  • This page was last modified on 19 February 2023, at 16:35.
  • This page has been accessed 514,828 times.
  • Privacy policy
  • About cppreference.com
  • Disclaimers

Powered by MediaWiki

std:: visit

Applies the visitor vis to the variants vars

Effectively returns

std:: invoke ( std:: forward < Visitor > ( vis ) , std :: get < is > ( std:: forward < Variants > ( vars ) ) ... )

, where is... is vars.index()... .

Return value

Throws std::bad_variant_access if any variant in vars is valueless_by_exception .

When the number of variants is zero or one, the invocation of the callable object is implemented in constant time, i.e. it does not depend on sizeof...(Types) .

If the number of variants is larger than 1, the invocation of the callable object has no complexity requirements.

MC++ BLOG

Visiting a std::variant with the Overload Pattern

Typically, you use the overload pattern for a std::variant . std::variant is a type-safe union. A std::variant (C++17) has one value from one of its types. std::visit allows you to apply a visitor to it. Exactly here comes the overload pattern convenient into play.

templates

In my last post, “ Smart Tricks with Parameter Packs and Fold Expressions “, I introduced the overload pattern as a smart trick to create an overload set using lambdas. Typically, the overload pattern is used for visiting the value held by a std::variant .

I know from my C++ seminars that most developers don’t know std::variant and std::visit and still, use a union. Therefore, let me give you a quick reminder about std::variant and std::visit .

std::variant (C++17)

A std::variant is a type-safe union. An instance of std::variant has a value from one of its types. The value must not be a reference, C-array, or void. A std::variant can have one type more than once. A default-initialized std::variant will be initialized with its first type. In this case, the first type must have a default constructor. Here is an example based on cppreference.com.  

I define both variants v and w . They can have an int and a float value. Their initial value is 0. v becomes 12 (line 1). std::get<int>(v) returns the value. In lines (2) – (3), you see three possibilities to assign variant v the variant w. But you have to keep a few rules in mind. You can ask for the value of a variant by type (line 5) or index (line 6). The type must be unique and the index valid. On line 7, the variant w holds an int value. Therefore, I get a std::bad_variant_access exception. If the constructor or assignment call is unambiguous, a simple conversion occurs. That is the reason that it’s possible to construct a std::variant<std::string> in line (8) with a C-string or assign a new C-string to the variant (line 9).

Of course, there is way more about std::variant. Read the post “ Everything You Need to Know About std::variant from C++17 ” by Bartlomiej Filipek.

Modernes C++ Mentoring

  • " Fundamentals for C++ Professionals " (open)
  • " Design Patterns and Architectural Patterns with C++ " (open)
  • " C++20: Get the Details " (open)
  • " Concurrency with Modern C++ " (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025

Do you want to stay informed: Subscribe.

Thanks to the function std::visit , C++17 provides a convenient way to visit the elements of a std::variant .

According to the classical design patterns, what sounds like the visitor pattern is a kind of visitor for a container of variants.

std::visit allows you to apply a visitor to a container of variants. The visitor must be callable. A callable is something that you can invoke. Typical callables are functions, function objects, or lambdas. I use lambdas in my example.

I create in (1) a std::vector of variants and initialize each variant. Each variant can hold a char, long, float, int, double, or long long value. It’s pretty easy to traverse the vector of variants and apply the lambda (lines (2) and (3) to it. First, I display the current value (2), and second, thanks to the call typeid(arg).name() (3), I get a string representation of the type of the current value.

visitVariants

Fine? No!. I used it in the program visitVariant.cpp generic lambda. Consequently, the string representations of the types are pretty unreadable using GCC: “ i c d x l f i “. Honestly, I want to apply a specific lambda to each type of the variant. Now, the overload pattern comes to my rescue.

Overload Pattern

Thanks to the overload pattern, I can display each type with a readable string and display each value in an appropriate way.

Line (1) creates a vector of variants having integral types, and line (4) creates a vector of variants having a std::vector<int> , double , and a std::string .

Let me continue with the first variant vecVariant . TypeOfIntegral (2) is an overload set that returns for a few integral types a string representation. If the type is not handled by the overload set, I return the string “ unknown type “. In line (3), I apply the overload set to each variant v using std::visit .

The second variant vecVariant2 (4), has composed types. I create an overload set (5) to display their values. In general, I can just push the value onto std:.cout . For the std::vector<int> , I use a range-based for-loop to push its values to std::cout .

Finally, here is the output of the program.

visitVariantsOverloadPattern

I want to add a few words to the overload pattern used in this example (7). I already introduced in my last post, “ Smart Tricks with Parameter Packs and Fold Expressions `.

Line (1) is the overload pattern, and line (2) is the deduction guide. The struct Overload can have arbitrarily many base classes ( Ts ... ). It derives from each class public and brings the call operator ( Ts::operator.. .) of each base class into its scope. The base classes need an overloaded call operator (Ts::operator()). Lambdas provide this call operator. The following example is as simple as it can be.

Using this example in C++ Insights makes the magic transparent. First, call (1) causes the creation of a fully specialized class template.

OverloadPatternInstantiated

Second, the used lambdas in the overload pattern such as [](char) { return "char"; } cause the creation of a function object. In this case, the compiler gives the function object the name __lambda_15_9 .

lambdaChar

Studying the auto-generate types show at least one interesting point. The call operator of __lambda_15_9 is overloaded for char: const char * operator() (char) const { return "char"; }

The deduction guide ( template<class... Ts> Overload(Ts...) -> Overload<Ts...>; ) (line 2) is only needed for C++17. The deduction guide tells the compiler how to create out-of-constructor arguments template parameters. C++20 can automatically deduce the template. 

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

  • C++ – The Core Language
  • C++ – The Standard Library
  • C++ – Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++
  • Clean Code with Modern C++

Online Seminars (German)

  • Embedded Programmierung mit modernem C++   (24. Sep. 2024 bis 26. Sep. 2024)
  • Mobil: +49 176 5506 5086
  • Mail: [email protected]
  • German Seminar Page: www.ModernesCpp.de
  • Mentoring Page: www.ModernesCpp.org

Modernes C++ Mentoring,

cppreference std visit

In first example, as I expected, gcc doesn’t let std::variant be constructed because it’s a conflicting declaration.

error: conflicting declaration std::variant v(“abc”) ; note: previous declaration as ‘std::variant v’ std::variant v, w ;

Leave a Reply

Leave a reply cancel reply.

Your email address will not be published. Required fields are marked *

templates

Applies the visitor vis ( Callable that can be called with any combination of types from variants) to the variants vars .

These overloads participate in overload resolution only if every type in std:: remove_reference_t < Variants > ... is a (possibly const-qualified) specialization of std::variant , or a (possibly const-qualified) class C such that there is exactly one std::variant specialization that is a base class of C and it is a public and unambiguous base class.

Effectively returns.

std:: invoke ( std:: forward < Visitor > ( vis ) , std:: get < is > ( std:: forward < VariantBases > ( vars ) ) ... ) .

, where every type in VariantBases... is the unique std::variant specialization determined above, except that const , & , or && is added to it if the corresponding argument is of a const-qualified type, is an lvalue, or is an rvalue, respectively, and is... is std:: forward < VariantBases > ( vars ) . index ( ) ... .

Return value

Throws std::bad_variant_access if any variant in vars is valueless_by_exception . Whether any variant is valueless by exception is determined as if by ( std:: forward < VariantBases > ( vars ) . valueless_by_exception ( ) || ... ) .

When the number of variants is zero or one, the invocation of the callable object is implemented in constant time, i.e. it does not depend on sizeof...(Types) .

If the number of variants is larger than 1, the invocation of the callable object has no complexity requirements.

Let n be ( 1 * ... * std:: variant_size_v < std:: remove_reference_t < VariantBases >> ) , implementations usually generate a table equivalent to an (possibly multidimensional) array of n function pointers for every specialization of std::visit , which is similar to the implementation of virtual functions .

Implementations may also generate a switch statement with n branches for std::visit (e.g. the MSVC STL implementation uses a switch statement when n is not greater than 256).

On typical implementations, the time complexity of the invocation of vis can be considered equal to that of access to an element in an (possibly multidimensional) array or execution of a switch statement.

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

© cppreference.com Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0. https://en.cppreference.com/w/cpp/utility/variant/visit

cppreference std visit

Write clean and maintainable C++

cppreference std visit

Modern C++ Features – std::variant and std::visit

std::variant is a library addition in C++17 for sum types, and std::visit is one of the ways to process the values in a std::variant .

What is a sum type? Sum types are compound types that have a range of values that is the sum of the ranges of their parts. Typically, e.g. when we have a struct or std::tuple , we deal with product types, where the range of values is the product of the ranges of its parts. Let’s look at a simple example:

unsigned char has the range from 0 through 255, and bool can have the values true and false . Those are 256 and 2 values, respectively. The struct P is the cartesian product of the two and can have 256×2 = 512 values.

A sum type of unsigned char and bool would not have a range of 512 values but 258: It can have either one of the 256 values of unsigned char or one of the two values of bool . If you have worked with C or low-level C++ you probably already know a way to build sum types: Unions are sum types.

std::variant as a “safer union”

There are several problems with unions: The biggest one is that they don’t respect object lifetimes in the sense that constructors or destructors are not called when you change the contained type. The other one is that accessing the wrong element is undefined behavior. Imagine a union that contains a nontrivial type:

The access in the last line is undefined behavior, which means anything can happen – but mostly bad things will happen. The best we can hope for is an immediate crash which is likely in this scenario. Worse are more subtle errors where we don’t see any obvious problem but get wrong results.

With std::variant , we have a safe alternative. The above code would look similar to this:

How it works

std::variant stores some information about the currently “active” type. While that means it needs some extra memory (a byte or so), this extra memory is well spent. In the interface of std::variant , the index is the number that defines which of the alternative types are stored in the variant. So, in the little example above, the index of io is 0 after the construction, because std::vector<int> is the first type in the list. After the assignment with the double, the index is 1.

Access std::variant by index

The currently active index can be obtained by the member function variant::index() . If we know the index at compile time, we can get the value stored in the variant using std::get<I> . std::get will throw an exception of type std::bad_variant_access if I is not the active index.

std::variant also provides means to construct the element with a given index in place. For that, we have constructors that take a std::in_place_index_t<I> as first parameter, and an emplace member function taking I as the first template parameter:

Access std::variant by type

The accesses described above can in general also be done by type. You have seen std::get<T> in action above, there is an emplace<T> , and an std::in_place_type_t<T> for the constructor. Instead of the index() member function, there is a free function std::holds_alternative<T> that takes a std::variant and returns a bool , stating whether the currently held value is of type T .

Variants can have the same type more than once in their type list, e.g. std::variant<int, double, int> . In that case, the access by type is ambiguous and not allowed. “Not allowed” here means that the emplacement functionality does not take part in overload resolution and std::get<T> and std::holds_alternative<T> are ill-formed.

Doing something with a variant where we do not know the active index at compile time can be tedious, if done manually. Imagine having to write an output operator:

The problem here is that we query the index at run time, but we need to know the template argument for std::get at compile time. Type based access won’t help us here, either.

The solution to the problem is std::visit : It is a free function that takes a visitor as first parameter, and one or more variants. The visitor has to be a callable that accepts any type that may be stored by the passed variants. The above operator can, for example, then be implemented like this:

In this example, the visitor is a generic lambda . If we want to be less generic, we’ll have to create a function object with different overloads of the function call operator:

With std::variant we have type safe sum types in C++17, and with std::visit we have an elegant way to deal with whatever we have stored in there.

If you’d like to see a nice way to compose visitors on the fly, using a nice mix of modern features, stay tuned for the next blog post!

Further reading:

If you want to know more about std::variant , there’s an exhaustive post by Bartlomiej Filipek .

' src=

I admit I do not see a big value in getting std::variant in the stl , and the reason is the exceptionally ( pan intended ) poor/restrictive interface. I cannot, for life of me, find any non-academic usage example of this contraption , all the examples , including this one, are far outside of real life usage in my opinion . Does anyone know of a good , non-academic example ?

' src=

std::visit allows for passing multiple std::variant<> s in one call. What’s the use case for that? Also, what the heck is returned? Also, in what order are the multiple variant arguments visited? (cppreference doesn’t answer any of these questions) (maybe you have another post explaining this?)

' src=

If you pass multiple variants the visited function will receive multiple arguments. You’ll have to provide an overload for each combination of variant types.

' src=

Thanks for the guide! I used it to write https://github.com/phylovi/libsbn/blob/897c2e6668b3c8dc5d05558368fd2d746382c61f/src/gp_engine.hpp

  • Overload: Build a Variant Visitor on the Fly - 6 years ago Permalink

' src=

What will happen if we have a variant with two int types and want to use std::visit?

' src=

If you have the same type twice in a variant, the same overload will be selected for both.

' src=

According to cppreference: “Variant is not allowed to allocate additional (dynamic) memory.” Does that not preclude holding a vector-type?

No, it doesn’t. The emphasis is on additional , i.e. beyond what the elements allocate. You can also say that the variant does not allocate memory here, it’s the vector held by it.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Using the Visitor pattern in C++17

Richard Haar

In C++17, the std::variant was introduced alongside the std::visit function which allows you to implement the Visitor pattern rather easily. To begin with I'll explain how to use the std::variant and how it fit's into the Visitor pattern.

Basic Usage #

A std::variant is templated across multiple types, so that your variant can hold any one of those types. Have a look at the example below where the variant can hold either a std::string or an int .

Calling std::get with the wrong type will throw an error.

Similar to how TypeScript defines the types allowed for a given variable, a std::variant allows the usage of multiple types.

A Shapes Example #

Here is a slightly more involved example, where three shapes are defined, with no common base class. And a vector is defined holding any of the three shapes.

Now the visitor pattern is setup so that to quote wikipedia :

It should be possible to define a new operation for (some) classes of an object structure without changing the classes.

So here is where std::visit comes into play, where an overloaded function can be applied to a std::variant . So for example we could use a lambda or define a struct that has an operator() defined for each type we need. For example:

Triangle area is calculated based on an equilateral triangle

And we can apply the function by using std::visit as follows:

Giving us our result:

When to use the Visitor pattern? #

The Visitor pattern can be useful when you need to constantly add new operations to your set of types. For example, now it would be possible to add another function making use of the shape's size without affecting the previous code (e.g. a function ValidShape checking if the shape has a length or radius greater than zero). However adding new types may mean you have to revisit all your previous functions you have defined.

Comparison to polymorphism #

The typical Shape example consists of having a base class Shape which then has virtual functions such as CalculateArea . Then each class would have to implement the virtual function if it was pure virtual. Whereas with the Visitor pattern you can add functions to given classes as needed.

For more examples see cppreference:

  • std::variant
  • Basic Usage
  • When to use the Visitor pattern?
  • Comparison to polymorphism

That `overloaded` Trick: Overloading Lambdas in C++17

C++17 has granted us with std::variant . Simply put, it is a type-safe union . To access the value it stores, you can either request a specific type (using std::get or something similar) or “visit” the variant, automatically handling only the data-type that is actually there. Visiting is done using std::visit , and is fairly straight forward.

Compilation , Execution

In (1) we define our variant type. In (2) we define a class with an overloaded operator() . This is needed for the call to std::visit . In (3) we define a vector of variants. In (4) we visit each variant. We pass in an instance of Print , and overload resolution ensures that the correct overload will be called for every type. But this example forces us to write and name an object for the overloaded operator() . We can do better. In fact, the example for std::visit on cppreference already does. Here is an example derived from it:

This is certainly more compact, and we removed the Print struct. But how does it work? You can see a class-template (1), lambdas passed in as arguments for the construction (3), and something with an arrow and some more template magic (2). Let’s build it step by step.

First, we want to break the print functions out of Print and compose them later.

In (1) and (2), we define the same operators as before, but in separate structs. In (3), we are inherit from both of those structs, then explicitly use their operator() . This results in exactly the same results as before. Next, we convert Print into a class template. I’ll jump ahead and convert it directly to a variadic template.

In (1) we define the template. We take an arbitrary number of classes, inherit from them, and use their operator() . In (2) we instantiate the Print class-template with PrintCString and PrintInt to get their functionality. Next, we want to use lambdas to do the same. This is possible because lambdas are not functions; they are objects implementing operator() .

In (1) we define the lambdas we need. In (2) we instantiate the template with our lambdas. This is ugly. Since lambdas have unique types, we need to define them before using them as template parameters (deducing their types using decltype ). Then, we need to pass the lambdas as arguments for aggregate initialization as lambdas have a delete default constructor. We are close, but not quite there yet. The <decltype(PrintCString), decltype(PrintInt)> part is really ugly, and causes repetition. But it is needed as ctors cannot do type-deduction. So in proper C++ style, we will create a function to circumvent that.

In (1) we define our helper function, to perform type deduction and forward it to the ctor. In (2) we take advantage of our newly found type-deduction to define the lambdas inline. But this is C++17, and we can do better.

C++17 added user-defined deduction guides . Those allow us to instruct the compiler to perform the same actions as our helper function, but without adding another function. Using a suitable deduction guide, the code is as follows.

In (1) we define a deduction guide which acts as our previous helper function, and in (2) we use the constructor instead of a helper function. Done.

Now we have fully recreated the original example. As Print is no longer indicative of the template-class’ behavior, overloaded is probably a better name.

std:: visit

Applies the visitor vis to the variants vars

Effectively returns

std:: invoke ( std:: forward < Visitor > ( vis ) , std :: get < is > ( std:: forward < Variants > ( vars ) ) ... )

, where is... is vars.index()... . The return type is deduced from the returned expression as if by decltype .

The call is ill-formed if the invocation above is not a valid expression of the same type and value category, for all combinations of alternative types of all variants.

Return value

The value returned by the selected invocation of the visitor.

Throws std::bad_variant_access if any variant in vars is valueless_by_exception .

When the number of variants is zero or one, the invocation of the callable object is implemented in constant time, i.e. it does not depend on sizeof...(Types) .

If the number of variants is larger than 1, the invocation of the callable object has no complexity requirements.

Stay up-to-date with Modern C++

cppreference std visit

Last Update: 23 January 2023

Everything You Need to Know About std::variant from C++17

cppreference std visit

Table of Contents

Around the time C++17 was being standardized I saw magical terms like “discriminated union”, “type-safe union” or “sum type” floating around. Later it appeared to mean the same type: “variant”.

Let’s see how this brand new std::variant from C++17 works and where it might be useful.

Update in early 2023 with notes about C++20, C++23 and other smaller refinements/wording.

Something better than union  

But lest first start with something old… unions …

Unions are rarely used in the client code, and most of the time, they should be avoided.

For example, there’s a “common” trick with floating-point operations:

However, while the above code might work in C99, due to stricter aliasing rules it’s undefined behaviour in C++!

There’s an existing Core Guideline Rule on that C.183 :

C.183: Don’t use a union for type punning : It is undefined behaviour to read a union member with a different type from the one with which it was written. Such punning is invisible, or at least harder to spot than using a named cast. Type punning using a union is a source of errors.

There’s also additional issue with unions: they’re very simple and crude. You don’t have a way to know what’s the currently used type and what’s more they won’t call destructors of the underlying types. Here’s an example from cppreference/union that clearly illustrate how hard it might be:

Play with the code @Coliru

As you see, the S union needs a lot of maintenance from your side. You have to know which type is active and adequately call destructors/constructors before switching to a new variant.

That’s the reason you probably won’t see a lot of unions that use “advanced” types such as vectors, strings, containers, etc, etc. Union is mostly for basic types.

What could make unions better?

  • and the full support of their lifetime: if you switch the type then a proper destructor is called. That way we don’t leak.
  • a way to know what’s the active type

Before C++17 you could use some third-party library…. or use boost::variant . But now you have std::variant .

Basic demo of std::variant  

Here’s a basic demo of what you can do with this new type:

the output:

We have several things showed in the example above:

  • You know what’s the currently used type via index() or check via holds_alternative .
  • You can access the value by using get_if or get (but that might throw bad_variant_access exception)
  • Type Safety - the variant doesn’t allow to get a value of the type that’s not active
  • If you don’t initialize a variant with a value, then the variant is initialized with the first type. In that case the first alternative type must have a default constructor.
  • No extra heap allocation happens
  • You can use a visitor to invoke some action on a currently hold type.
  • The variant class calls destructors and constructors of non-trivial types, so in the example, the string object is cleaned up before we switch to new variants.

When to Use  

I’d say that unless you’re doing some low-level stuff, possibly only with simple types, then unions might still be ok. But for all other uses cases, where you need variant types, std::variant is a way to go!

Some possible uses:

  • All the places where you might get a few types for a single field: so things like parsing command lines, ini files, language parsers, etc, etc.
  • Expressing efficiently several possible outcomes of a computation: like finding roots of equations
  • Error handling - for example you can return variant<Object, ErrorCode> . If the value is available, then you return Object otherwise you assign some error code (as of C++23 you can use std::expected ).
  • State machines
  • Polymorphism without vtables and inheritance (thanks to visiting pattern)

A Functional Background  

It’s also worth mentioning that variant types (also called a tagged union, a discriminated union, or a sum type) comes from the functional language world and Type Theory .

After a little demo and introduction, we can now talk about some more details… so read on.

The Series  

This article is part of my series about C++17 Library Utilities. Here’s the list of the other topics that I’ll cover:

  • Refactoring with std::optional
  • Using std::optional
  • Error handling and std::optional
  • About std::variant
  • About std::any
  • In place construction for std::optional , std::variant and std::any
  • std::string_view Performance
  • C++17 string searchers & conversion utilities (this post)
  • Working with std::filesystem
  • Show me your code: std::optional
  • A Wall of Your std::optional Examples
  • Menu Class - Example of Modern C++17 STL features

Plus other 110+ articles with the C++17 tag @C++Stories .

cppreference std visit

Resources about C++17 STL:

  • C++17 In Detail by Bartek!
  • C++17 - The Complete Guide by Nicolai Josuttis
  • C++ Fundamentals Including C++ 17 by Kate Gregory
  • Practical C++14 and C++17 Features - by Giovanni Dicanio
  • C++17 STL Cookbook by Jacek Galowicz

std::variant Creation  

There are several ways you can create and initialize std::variant :

Play with the code here @Compiler Explorer

The output:

  • if that’s not possible when the type doesn’t have a default constructor, then you’ll get a compiler error
  • you can use std::monostate to pass it as the first type in that case
  • if there’s an ambiguity, then you can use a version std::in_place_index to explicitly mention what type should be used.
  • std::in_place also allows you to create more complex types and pass more parameters to the constructor

About std::monostate  

In the example you might notice a special type called std::monostate . It’s just an empty type that can be used with variants to represent empty state. The type might be handy when the first alternative doesn’t have a default constructor. In that situation you can place std::monostate as the first alternative.

Changing values  

There are four ways to change the current value of the variant:

  • the assignment operator
  • get and then assign a new value for the currently active type

The important part is to know that everything is type safe and also the object lifetime is honoured.

See the live example @Coliru

Object Lifetime  

When you use union , you need to manage the internal state: call constructors or destructors. This is error prone and easy to shoot yourself in the foot. But std::variant handles object lifetime as you expect. That means that if it’s about to change the currently stored type then a destructor of the underlying type is called.

Or see this example with a custom type:

This will produce the following output:

At the start, we initialize with a default value of type MyType ; then we change the value with an instance of OtherType , and before the assignment, the destructor of MyType is called. Later we destroy the temporary object and the object stored in the variant.

Accessing the Stored Value  

From all of the examples, you’ve seen so far you might get an idea how to access the value. But let’s make a summary of this important operation.

First of all, even if you know what’s the currently active type you cannot do:

So you have to use helper functions to access the value.

You have std::get<Type|Index>(variant) which is a non member function. It returns a reference to the desired type if it’s active (You can pass a Type or Index). If not then you’ll get std::bad_variant_access exception.

The next option is std::get_if . This function is also a non-member and won’t throw. It returns a pointer to the active type or nullptr . While std::get needs a reference to the variant, std::get_if takes a pointer. I’m not sure why we have this inconsistency.

However, probably the most important way to access a value inside a variant is by using visitors.

Visitors for std::variant  

With the introduction of std::variant we also got a handy STL function called std::visit .

It can call a given “visitor” on all passed variants.

Here’s the declaration:

And it will call vis on the currently active type of variants.

If you pass only one variant, then you have to have overloads for the types from that variant. If you give two variants, then you have to have overloads for all possible pairs of the types from the variants.

A visitor is “a callable that accepts every possible alternative from every variant “.

Let’s see some examples:

In the above example, a generic lambda is used to generate all possible overloads. Since all of the types in the variant supports << then we can print them.

In the another case we can use a visitor to change the value:

Generic lambdas can work if our types share the same “interface”, but in most of the cases, we’d like to do some different actions based on an active type.

That’s why we can define a structure with several overloads for the operator () :

In the example, you might notice that I’ve used a state to hold the desired scaling factor value.

With lambdas, we got used to declaring things just next to its usage. And when you need to write a separate structure, you need to go out of that local scope. That’s why it might be handy to use overload construction.

Overload  

With this utility you can write all several lambdas for all matching types in one place:

Currently this helper is not part of the library ( it might get into with C++20 ), but the code might look like that:

Those two lines look like a bit of magic :) But all they do is they create a struct that inherits all given lambdas and uses their Ts::operator() . The whole structure can be now passed to std::visit .

For example:

Here’s a full blog post explaining the feature: 2 Lines Of Code and 3 C++17 Features - The overload Pattern - C++ Stories .

And other articles:

  • Arne Mertz wrote more about this technique in his recent post: SimplifyC++ - Overload: Build a Variant Visitor on the Fly .
  • And here’s the paper for the proposal of std::overload : P0051 - C++generic overload function
  • Also, if you’d like to know how std::visit works underneath, then you might want to check out this post: Variant Visitation by Michael Park

Other std::variant Operations  

Just for the sake of completeness:

  • if they contain the same active alternative then the corresponding comparison operator is called.
  • If one variant has an “earlier” alternative then it’s “less than” the variant with the next active alternative.
  • Variant is a value type, so you can move it .
  • std::hash on a variant is also possible.

Exception Safety Guarantees  

So far everything looks nice and smooth… but what happens when there’s an exception during the creation of the alternative in a variant?

For example

In the first case - with the assignment operator - the exception is thrown in the constructor of the type. This happens before the old value is replaced in the variant, so the variant state is unchanged. As you can see we can still access int and print it.

However, in the second case - emplace - the exception is thrown after the old state of the variant is destroyed. Emplace calls operator int to replace the value, but that throws. After that, the variant is in a wrong state, as we cannot recover.

Also note that a variant that is “valueless by exception” is in an invalid state. Accessing a value from such variant is not possible. That’s why variant::index returns variant_npos , and std::get and std::visit will throw bad_variant_access .

Performance & Memory Considerations  

std::variant uses the memory in a similar way to union: so it will take the max size of the underlying types. But since we need something that will know what’s the currently active alternative, then we need to add some more space.

Plus everything needs to honour the alignment rules.

Here are some basic sizes:

On GCC 8.1, 32 bit I have:

What’s more interesting is that std::variant won’t allocate anyextra space ! No dynamic allocation happens to hold variants. and the discriminator.

While you pay some extra space for all the type-safe functionality, it shouldn’t cost you regarding runtime performance.

Migration From boost::variant  

Boost Variant was introduced around the year 2004, so it was 13 years of experience before std::variant was added into the Standard. The STL type takes from the experience of the boost version and improves it.

Here are the main changes:

You can also see the slides from Variants - Past, Present, and Future - David Sankel - CppCon 2016 Where there is more discussion about the changes and the proposal.

Or the video @Youtube

Examples of std::variant  

After we learned most of the std::variant details, we can now explore a few examples. So far, the code I used was a bit artificial, but in this section, I tried to look for some real-life examples.

Error Handling  

The basic idea is to wrap the possible return type with some ErrorCode, and that way allow to output more information about the errors. Without using exceptions or output parameters. This is similar to what std::expected might be in C++23..

Play with the example @Coliru

In the example, I’m returning ErrorCode or a valid type - in this case, a string.

In C++23 you can use std::expected . See at std::expected - cppreference.com .

Computing Roots of an Equation  

Sometimes the computation might give us several options, for example, real roots of the equation. With variant, we can wrap all the available options and express clearly how many roots can we find.

Play with the code @Compiler Explorer .

Parsing a Command Line  

Command line might contain text arguments that might be interpreted in a few ways:

  • as boolean flag
  • as a string (not parsed)

We can build a variant that will hold all the possible options.

Here’s a simple version with int and string :

And the parsing code:

At the moment of writing, std::from_chars in GCC only supports integers, in MSVC floating point support is on the way. But the idea of the TryParseString is to try with parsing the input string to the best matching type. So if it looks like an integer, then we try to fetch integer. Otherwise, we’ll return an unparsed string. Of course, we can extend this approach.

Example how we can use it:

Parsing a Config File  

I don’t have a code for that, but the idea comes from the previous example of a command line. In the case of a configuration file, we usually work with pairs of <Name, Value> . Where Value might be a different type: string , int , array, bool , float , etc.

In my experience I’ve seen examples where even void* was used to hold such unknown type so we could improve the design by using std::variant if we know all the possible types, or leverage std::any .

State Machines  

How about modelling a state machine? For example door’s state:

cppreference std visit

We can use different types of states and the use visitors as events:

And here are the events:

Play with the code using the following example: @Coliru

Would you like to see more? I extended the code from this section and explored Finite State Machines with std::variant . See the first about "enum style FSM into variant-based" or the second article, which are available for C++ Stories Premium/Patreon members. See all Premium benefits here .

The idea is based on the blog posts:

  • Sum types and state machines in C++17
  • Implementing State Machines with std::variant

Polymorphism  

Most of the time in C++ we can safely use runtime polymorphism based on v-table approach. You have a collection of related types - that shares the same interface, and you have a well defined virtual method that can be invoked.

But what if you have “unrelated” types that don’t share the same base class? What if you’d like to quickly add new functionality without changing the code of the supported types?

In such situations, we have a handy pattern of Visitor. I’ve even described in my older post .

With std::variant and std::visit we can build the following example:

Play with the code: @Coliru

In the above example, I’ve shown only the first case of invoking a method from unrelated types. I wrap all the possible shape types into a single variant and then use a visitor to dispatch the call to the proper type.

If you’d like, for example, to sort objects, then we can write another visitor, that holds some state. And that way you allow to have more functionality without changing the types.

I also have another article where I explain this kind of polymorphism in detail: Runtime Polymorphism with std::variant and std::visit - C++ Stories .

You can explore more about this pattern and its advantages in:

  • Another polymorphism | Andrzej’s C++ blog
  • Inheritance vs std::variant, C++ Truths

Other Uses  

There are many many more example, see this tweet:

do you have any real-life examples of std::variant? #cpp #cpp17 — Bartlomiej Filipek (@fenbf) 24 maja 2018

You can open this tweet and follow the discussion.

C++20 & C++23 improvements  

After a couple of years with C++20 and C++23 std::variant got a few updates. For example thanks to P2231 implemented against C++20, we can use std::variant in constant expressions:

See at Compiler Explorer .

Additionally there was also an important fix for cases like:

Now with A sane std::variant converting constructor you can expect that "abc" string literal will converst into std::string .

And you can look at other features like: std::visit() for classes derived from std::variant and std::variant and std::optional should propagate copy/move triviality .

Wrap Up  

After reading this post, you should be equipped with all the knowledge required to use std::variant in your projects!

While a similar type has been available for years - in the form of boost.variant - I’m happy to see the official STL version. That way we can expect more and more code that uses this handy wrapper type.

Here are the things to remember about std::variant :

  • It holds one of several alternatives in a type-safe way
  • No extra memory allocation is needed. The variant needs the size of the max of the sizes of the alternatives, plus some little extra space for knowing the currently active value.
  • By default, it initializes with the default value of the first alternative
  • You can assess the value by using std::get , std::get_if or by using a form of a visitor.
  • To check the currently active type you can use std::holds_alternative or std::variant::index
  • std::visit is a way to invoke an operation on the currently active type in the variant. It’s a callable object with overloads for all the possible types in the variant(s).
  • Rarely std::variant might get into invalid state, you can check it via valueless_by_exception

I’d like to thank Patrice Roy ( @PatriceRoy1 ), Mandar Kulkarni ( @mjkcool ) for finding time to do a review of this article!

See also some other posts about std::variant :

  • C++17 has a Visitor Modernes C++
  • My take on variant - Jonathan Müller

Back to you

  • Have you tried std::variant ?
  • What’s the most common use case for you?

Let us know in the comments below.

I've prepared a valuable bonus if you're interested in Modern C++! Learn all major features of recent C++ Standards! Check it out here:

Similar Articles:

  • Error Handling and std::optional
  • Space Game: A std::variant-Based State Machine by Example
  • Speeding up Pattern Searches with Boyer-Moore Algorithm from C++17
  • How to Use The Newest C++ String Conversion Routines - std::from_chars

IMAGES

  1. How To Use std::visit With Multiple Variants and Parameters : r/cpp

    cppreference std visit

  2. Std Array

    cppreference std visit

  3. 为什么cppreference上说std::printf是表达式?

    cppreference std visit

  4. C++ : A std::visit-like function for visiting over polymorphic types

    cppreference std visit

  5. A Guide to STD Treatment and More

    cppreference std visit

  6. New STD Testing Method Giving Men More Privacy During Screening

    cppreference std visit

VIDEO

  1. Лекция 57. Unions, std::variant, std::visit. Overload pattern

  2. Лекция 58. Внутреннее устройство std::variant и std::visit

  3. Why is C++ documentation SO BAD?! #cpp #cppreference #coding

  4. C++ From Scratch: Parallel STL Algorithms

  5. C++ Beginner Tutorial

  6. C++ Beginner Tutorial

COMMENTS

  1. std::visit

    On typical implementations, the time complexity of the invocation of vis can be considered equal to that of access to an element in an (possibly multidimensional) array or execution of a switch statement. Feature-test macro. Value. Std. Feature. __cpp_lib_variant. 202102L. (C++17)(DR) std::visit for classes derived from std::variant.

  2. std::variant<Types...>::visit

    2) Nothing if R is (possibly cv-qualified) void; otherwise the result of the std:: visit < R > invocation. [ edit ] Exceptions Only throws if the call to std:: visit throws.

  3. How To Use std::visit With Multiple Variants and Parameters

    Since we can use a functor object, then a similar thing can be done with a lambda! What we can do is we can write a generic lambda that captures the parameter. And now we can try std::visit with the following code: int param = 10; std::visit(overload{ [&param](const auto& item) { checkParam(item, param); }, }, packet);

  4. std::visit

    Return value. The value returned by the selected invocation of the visitor, converted to the common type of all possible std::invoke expressions.. Exceptions. Throws std::bad_variant_access if any variant in vars is valueless_by_exception.. Complexity

  5. std::visit

    Applies the visitor vis (Callable that can be called with any combination of types from variants) to the variants vars.. These overloads participate in overload resolution only if every type in std:: remove_reference_t < Variants >... is a (possibly const-qualified) specialization of std::variant, or a (possibly const-qualified) class C such that there is exactly one std::variant ...

  6. How does std::visit work with std::variant?

    Based on symbol names in compiler output (libstdc++), the array of function pointers is actually a static array of invoker objects that will call std::get<T>on each of the variant with T corresponding to one of the invoker template arguments. The same callable is then used by all invokers on the casted variants.

  7. std::visit

    Exceptions. Throws std::bad_variant_access if any variant in vars is valueless_by_exception.. Complexity. When the number of variants is zero or one, the invocation of the callable object is implemented in constant time, i.e. it does not depend on sizeof...(Types).. If the number of variants is larger than 1, the invocation of the callable object has no complexity requirements.

  8. std::variant

    A program that instantiates the definition of std::variant with no template arguments is ill-formed. std:: variant < std:: monostate > can be used instead. If a program declares an explicit or partial specialization of std::variant, the program is ill-formed, no diagnostic required.

  9. Visiting a std::variant with the Overload Pattern

    Typically, you use the overload pattern for a std::variant.std::variant is a type-safe union. A std::variant (C++17) has one value from one of its types. std::visit allows you to apply a visitor to it. Exactly here comes the overload pattern convenient into play. In my last post, "Smart Tricks with Parameter Packs and Fold Expressions", I introduced the overload pattern as a smart trick to ...

  10. Std::visit

    * std:: variant_size_v < std:: remove_reference_t < VariantBases >>), implementations usually generate a table equivalent to an (possibly multidimensional) array of n function pointers for every specialization of std::visit, which is similar to the implementation of virtual functions.

  11. Modern C++ Features

    With std::variant we have type safe sum types in C++17, and with std::visit we have an elegant way to deal with whatever we have stored in there. If you'd like to see a nice way to compose visitors on the fly, using a nice mix of modern features, stay tuned for the next blog post!

  12. Using the Visitor pattern in C++17

    In C++17, the std::variant was introduced alongside the std::visit function which allows you to implement the Visitor pattern rather easily. To begin with I'll explain how to use the std::variant and how it fit's into the Visitor pattern.. Basic Usage#. A std::variant is templated across multiple types, so that your variant can hold any one of those types. . Have a look at the example below ...

  13. std::visit_format_arg

    Defined in header <format>. template<class Visitor, class Context >/* see below */ visit_format_arg( Visitor&& vis, std::basic_format_arg<Context> arg ); (since C++20)(deprecated in C++26) Applies the visitor vis to the object contained in arg. Equivalent to std::visit(std::forward<Visitor>(vis), value), where value is the std::variant stored ...

  14. That `overloaded` Trick: Overloading Lambdas in C++17

    In (4) we visit each variant. We pass in an instance of Print, and overload resolution ensures that the correct overload will be called for every type. But this example forces us to write and name an object for the overloaded operator(). We can do better. In fact, the example for std::visit on cppreference already does. Here is an example ...

  15. std::visit

    Return value. The value returned by the selected invocation of the visitor. Exceptions. Throws std::bad_variant_access if any variant in vars is valueless_by_exception.. Complexity. When the number of variants is zero or one, the invocation of the callable object is implemented in constant time, i.e. it does not depend on sizeof...(Types).. If the number of variants is larger than 1, the ...

  16. std::list

    std::list is a container that supports constant time insertion and removal of elements from anywhere in the container. Fast random access is not supported. It is usually implemented as a doubly-linked list. Compared to std::forward_list this container provides bidirectional iteration capability while being less space efficient.. Adding, removing and moving the elements within the list or ...

  17. Everything You Need to Know About std::variant from C++17

    Around the time C++17 was being standardized I saw magical terms like "discriminated union", "type-safe union" or "sum type" floating around. Later it appeared to mean the same type: "variant". Let's see how this brand new std::variant from C++17 works and where it might be useful. Update in early 2023 with notes about C++20, C++23 and other smaller refinements/wording.

  18. C++ Variant visit overloaded function

    9. I want to execute an overloaded function over a variant. The following code block works and compiles, but the visit invocation seems overly complex. Why can I not simply write: std::visit(&f, something); Working version and context: #include <variant>. #include <string>. #include <iostream>.

  19. Confusing templates in C++17 example of std::visit

    template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; is a "deduction guide" (see this page for more details) and it's a new C++17 feature. In your case, the deduction guide says that when you write something as. auto ov = overloaded{ arg1, arg2, arg3, arg4 }; or also. overloaded ov{ arg1, args, arg3, arg4 };