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 Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Darshan Mody, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Wolfgang Gärtner,  Louis St-Amour, Stephan Roslen, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Avi Kohn, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, Sudhakar Balagurusamy, lennonli, and Pramod Tikare Muralidhara.

 

Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, and Dendi Suhubdy

 

Seminars

I'm happy to give online-seminars or face-to-face seminars world-wide. Please call me if you have any questions.

Bookable (Online)

Deutsch

English

Standard Seminars 

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

New

Contact Me

Modernes C++,

RainerGrimmSmall

 

Tags: C++17

Comments   

0 #1 viola de arco eagle 2017-11-14 02:22
Touche. Great arguments. Keep up the great effort.
Quote
0 #2 Isobel 2018-11-15 12:42
Hi, just wanted to tell you, I enjoyed this article.

It was practical. Keep on posting!
Quote

My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 2957

Yesterday 7515

Week 34568

Month 192999

All 5062313

Currently are 172 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments