C++17 has a Visitor
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 myself. In the post C++17 – What’s New in the Library are the details of the three data types that are part of C++17.
Construct in-place
What does construction in place mean? For simplicity reasons, 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.
Modernes C++ Mentoring
Be part of my mentoring programs:
Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.
// 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 a copy constructed from opt3.
Here is the output of the program.
Does in-place construction look unfamiliar to you? Why? We have had 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 a kind of 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 callable. A callable is something which you can invoke. Typically this can be a function, a function object, and a 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 pretty 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 where I apply various functions (visitors).
Now, I want to sum up the elements of the variants. At first, I need the correct 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 char, long, float, int, double, and long long can implicitly be converted. The final {} in res{} causes 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 Visual C++ quite readable names.
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 unavailable online. Therefore, I used in the first-step 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 algorithms. You will see in the next post which one.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, and Marco Parri Empoli.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | ![]() |
My special thanks to PVS-Studio | ![]() |
My special thanks to Tipi.build | ![]() |
My special thanks to Take Up Code | ![]() |
Seminars
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
Bookable
German
- Embedded Programmierung mit modernem C++ 12.12.2023 – 14.12.2023 (Präsenzschulung, Termingarantie)
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++
New
- Clean Code with Modern C++
- C++20
Contact Me
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,
Leave a Reply
Want to join the discussion?Feel free to contribute!