C++17 has a Visitor

Contents[Show]

What have std::optional, std::any, and std::variant in common? You can construct them in-place. But that is not everything. A std::variant supports a visitor.

But first of all. What's the job of the three new data types?

  • std::optional is a wrapper that may or may not hold an object.
  • std::variant is a type-safe union.
  • std::any is a type that may hold an object of an arbitrary type.

In order not to repeat me. In the post C++17 - What's New in the Library are the details to the three data types that are part of C++17.

timeline

Construct in-place

What does construction in place mean? For simplicity reason, I will refer only to std::optional. A std::optional<std::string> opt may hold a value of type std::string. You construct opt by only providing the arguments for the std::string constructor. 

A short example should make my point clear.

// inPlace.cpp

#include <optional>
#include <iostream>
#include <string>

int main(){
  
  std::cout << std::endl;
  
  // C string literal
  std::optional<std::string> opt1(std::in_place, "C++17");                        // 1

  // 5 characters 'C'
  std::optional<std::string> opt2(std::in_place,5, 'C');                          // 2

  // initializer list
  std::optional<std::string> opt3(std::in_place, {'C', '+', '+', '1', '7'});      // 3

  // Copy constructor
  std::optional<std::string> opt4(opt3);                                          // 4

  std::cout << *opt1 << std::endl;
  std::cout << *opt2 << std::endl;
  std::cout << *opt3 << std::endl;
  std::cout << *opt4 << std::endl;
  
  std::cout << std::endl;
    
}

 

opt1 (1), opt2 (2), and opt3 (3) are constructed with the tag std::in_place. This means that the constructor of std::string is invoked with the provided argument. Therefore, the strings are in-place constructed from a C string (1), 5 characters 'C', and an initializer list. This will not hold for opt4 (4). opt4 is copy constructed from opt3.

Here is the output of the program.

inPlace

Does in-place construction looks unfamiliar to you? Why? We have it since C++11. The containers of the Standard Template Library support a bunch of new methods for adding elements. These methods start with the name emplace such as emplace_back. Therefore you can add a new element to a std::vector<int> vec by just saying vec.emplace_back(5). This is equivalent to vec.push_back(int(5)).

What a coincidence! This week, I will give a seminar about design patterns in Python. And now, I found the std::visit function in the interface of std::variant. What sounds like the visitor pattern according to the classical design patterns is really a kind of a visitor for a list of variants.

Visit a list of variants

std::visit allows you to apply a visitor to a list of variants. The visitor must be a callable. A callable is something, which you can invoke. Typically this can be a function, a function object, and lambda function. For simplicity reasons, I use a lambda function in my example.

 

// visit.cpp

#include <iostream>
#include <vector>
#include <typeinfo>
#include <type_traits>

#include <variant>

  
int main(){
  
  std::cout << std::endl;
  
  std::vector<std::variant<char, long, float, int, double, long long>>      // 1
             vecVariant = {5, '2', 5.4, 100ll, 2011l, 3.5f, 2017};
  
  // display each value                                                             
  for (auto& v: vecVariant){
    std::visit([](auto&& arg){std::cout << arg << " ";}, v);                // 2
  }
  
  std::cout << std::endl;
  
  // display each type
  for (auto& v: vecVariant){
    std::visit([](auto&& arg){std::cout << typeid(arg).name() << " ";}, v); // 3
  }
  
  std::cout << std::endl;
  
  // get the sum
  std::common_type<char, long, float, int, double, long long>::type res{};  // 4
 
  std::cout << "typeid(res).name(): "  << typeid(res).name() << std::endl;  
  
  for (auto& v: vecVariant){
    std::visit([&res](auto&& arg){res+= arg;}, v);                          // 5
  }
  std::cout << "res: " << res << std::endl;
  
  // double each value
  for (auto& v: vecVariant){
    std::visit([&res](auto&& arg){arg *= 2;}, v);                           // 6
    std::visit([](auto&& arg){std::cout << arg << " ";}, v);
  }
   
  std::cout << std::endl;
  
}

 

I create in (1) a std::vector of variants. Each variant can hold a char, long, float, int, double, or long long. It's quite easy to traverse the vector of variants and apply the lambda function (2) to it. Thanks to the function typeid, I get the types to the variants. I think, you see the visitor pattern. The std::vector of variants is the visited data structure on which I apply various functions (visitors).

Now, I want to sum up the elements of the variants. At first, I need the right result type at compile time. std::common_type (4) from the type traits library will provide it for me. std::common_type gives me the type, to which all types char, long, float, int, double, and long long  can implicitly be converted to. The final {} in res{} causes that it will be initialized to 0.0. res is of type double. (5) calculates the sum. I can even use a visitor to change the elements on the fly. Have a look at (6).

Here is the output of the program. Run-time type information with std::type_info gives me with Visual C++ quite readable names.

visit

It was not so easy, to get this output. To compile the program, you need a current GCC snapshot. Which I don't have and is not available online. Therefore, I used in the first step the compiler explorer at godbolt to check the syntax of my program. In the second step, I compiled the program using the current Visual C++ compiler on http://webcompiler.cloudapp.net/. You have to use the flag std:c++latest. Two out of three runs produced a Maximum execution time exceeded! error. But finally, I made it.

What's next?

With C++17, we get Parallel Algorithm of the Standard Template Library. We even get a few new algorithm. You will see in the next post which one.

 

 

Thanks a lot to my Patreon Supporter: Eric Pederson.

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Get your e-book. Support my blog.

 

 

Tags: C++17

Add comment


My Newest E-Books

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 599

All 455653

Currently are 153 guests and no members online