{"id":543,"date":"2024-01-31T12:21:23","date_gmt":"2024-01-31T11:21:23","guid":{"rendered":"https:\/\/sii.ua\/blog\/?p=543"},"modified":"2024-02-16T09:43:01","modified_gmt":"2024-02-16T08:43:01","slug":"modern-approach-in-object-oriented-design-with-stdvariant-and-stdvisit-c17","status":"publish","type":"post","link":"https:\/\/sii.ua\/blog\/en\/modern-approach-in-object-oriented-design-with-stdvariant-and-stdvisit-c17\/","title":{"rendered":"Modern approach in object-oriented design with std::variant and std::visit (C++17)"},"content":{"rendered":"\n<p>Inheritance is one of the core concepts of object-oriented programming (OOP) languages. It is a mechanism where you can derive a class from another class for a hierarchy of classes that share a set of attributes and methods.<\/p>\n\n\n\n<p>It is a mechanism that allows developers to create a hierarchy between classes using an \u201eis-a\u201d relationship. The class being inherited from is called a parent class (base class) and the class inheriting is called the child class (derived class).<\/p>\n\n\n\n<p>Inheritance allows programmers to create classes built upon existing classes and specify a new implementation while maintaining the same behaviours.<\/p>\n\n\n\n<p>This article will demonstrate an alternative approach which will allow the creation of many polymorphic implementations with a minimal amount of code resulting in the same behaviour as in a well-known inheritance mechanism.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Runtime polymorphism with virtual functions<\/strong><\/h2>\n\n\n\n<p>When speaking of polymorphism immediately the first things that come to our mind are virtual functions and v-tables (C++).<\/p>\n\n\n\n<p>You declare a virtual function in a base class and then you override it in derived classes.<\/p>\n\n\n\n<p>When you call such a function on a reference or a pointer to the base class, then the compiler will invoke the correct overload. In most cases, compilers implement this technique with virtual tables (v-tables). Each class that has a virtual method contains an extra table that points to the addresses of the member functions. Before each call to a virtual method, the compiler needs to look at v-table and resolve the address of a derived function at runtime (it is called late method binding).<\/p>\n\n\n\n<p>An example below demonstrates a Vehicle base class with some interface and derived class (Pickup, Truck) which derive from it in a standard way.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Ryc.-1.png\"><img decoding=\"async\" width=\"686\" height=\"875\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Ryc.-1.png\" alt=\"code\" class=\"wp-image-546\" style=\"object-fit:cover\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Ryc.-1.png 686w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Ryc.-1-235x300.png 235w\" sizes=\"(max-width: 686px) 100vw, 686px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-3-1.png\"><img decoding=\"async\" width=\"636\" height=\"236\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-3-1.png\" alt=\"code\" class=\"wp-image-548\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-3-1.png 636w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-3-1-300x111.png 300w\" sizes=\"(max-width: 636px) 100vw, 636px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-2-1.png\"><img decoding=\"async\" width=\"387\" height=\"356\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-2-1.png\" alt=\"schema\" class=\"wp-image-550\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-2-1.png 387w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-2-1-300x276.png 300w\" sizes=\"(max-width: 387px) 100vw, 387px\" \/><\/a><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Pros and Cons of run-time polymorphism with virtual functions<\/strong><\/h2>\n\n\n\n<p><strong>Pros<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>it is very convenient to extend with new class,<\/li>\n\n\n\n<li>object orientation allows deep hierarchies,<\/li>\n\n\n\n<li>storing heterogeneous types in a single container is easy, just store pointers to the Base class or to the abstract class (interface).<\/li>\n<\/ul>\n\n\n\n<p><strong>Cons<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>virtual methods must be resolved before the call (performance overhead),<\/li>\n\n\n\n<li>since you need a pointer to call a method, usually it means dynamic allocation (more performance overhead),<\/li>\n\n\n\n<li>you need to modify the code of all classes in the inheritance structure.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Std::variant polymorphism<\/strong><\/h2>\n\n\n\n<p>The class template <strong>std::variant<\/strong>&nbsp;<strong>represents a type-safe union<\/strong>. An instance of <strong>std::variant<\/strong> at any given time either holds a value of one of its alternative types or in the case of error \u2013 no value.<\/p>\n\n\n\n<p>With this approach, there is no <strong>Vehicle<\/strong> class needed anymore as in the previous example. We can create more unrelated types here. The vehicles variable defines an object that can be a <strong>Pickup<\/strong> or <strong>Truck<\/strong>. In order to call a function we need a callable object and std::visit.<\/p>\n\n\n\n<p>A struct <strong>callPrintName<\/strong> implements overloads for the call operator. Then <strong>std::visit<\/strong> takes the variant object and calls the correct overload. std :: visit is a powerful tool that allows you to call functions on the current type stored inside std :: variant. It can find the appropriate function overloads, and what&#8217;s more, it works for multiple variants at once.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-4-1.png\"><img decoding=\"async\" width=\"527\" height=\"769\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-4-1.png\" alt=\"code\" class=\"wp-image-552\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-4-1.png 527w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-4-1-206x300.png 206w\" sizes=\"(max-width: 527px) 100vw, 527px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-5.png\"><img decoding=\"async\" width=\"290\" height=\"144\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-5.png\" alt=\"schema\" class=\"wp-image-554\"\/><\/a><\/figure>\n\n\n\n<p>This can be simplified and the callable object <strong>callPrintName<\/strong> can be replaced with generic lambdas if our variant subtypes share common interface.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-6-1.png\"><img decoding=\"async\" width=\"544\" height=\"169\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-6-1.png\" alt=\"code\" class=\"wp-image-556\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-6-1.png 544w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-6-1-300x93.png 300w\" sizes=\"(max-width: 544px) 100vw, 544px\" \/><\/a><\/figure>\n\n\n\n<p>What if we need some arguments? The main issues is that <strong>std::visit<\/strong> does not have a way to pass arguments into the callable object. It only takes a function object and a list of <strong>std::variant<\/strong> objects.<\/p>\n\n\n\n<p>One option is to create a custom functor:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-7-768x596-1.png\"><img decoding=\"async\" width=\"768\" height=\"596\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-7-768x596-1.png\" alt=\"code\" class=\"wp-image-558\" style=\"object-fit:cover\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-7-768x596-1.png 768w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-7-768x596-1-300x233.png 300w\" sizes=\"(max-width: 768px) 100vw, 768px\" \/><\/a><\/figure>\n\n\n\n<p>The other options to solve it by capturing the argument and forwarding it to a member function<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-8-1.png\"><img decoding=\"async\" width=\"604\" height=\"184\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-8-1.png\" alt=\"code\" class=\"wp-image-560\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-8-1.png 604w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-8-1-300x91.png 300w\" sizes=\"(max-width: 604px) 100vw, 604px\" \/><\/a><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The Overload pattern<\/strong><\/h2>\n\n\n\n<p>The overload pattern is useful for wrapping multiple separate lambdas into an overload set. The code is shorter and there is no need to declare a structure that holds <strong>operator()<\/strong> overloads. In C++17 it required 2 lines of code to be implemented:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-9-768x46-1.png\"><img decoding=\"async\" width=\"768\" height=\"46\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-9-768x46-1.png\" alt=\"code\" class=\"wp-image-562\" style=\"object-fit:cover\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-9-768x46-1.png 768w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-9-768x46-1-300x18.png 300w\" sizes=\"(max-width: 768px) 100vw, 768px\" \/><\/a><\/figure>\n\n\n\n<p>The first line is the overload pattern, the second is the deduction guide for it. The struct <strong>Overloaded<\/strong><\/p>\n\n\n\n<p>can have arbitrary many base classes. It derives from each class and brings the call operator(<strong>Ts::operator..<\/strong>) of each base class into the scope. The base classes need an overloaded call operator (<strong>Ts::operator())<\/strong>. Lambdas provide this call operator. <\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-10-1.png\"><img decoding=\"async\" width=\"591\" height=\"237\" src=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-10-1.png\" alt=\"code\" class=\"wp-image-564\" srcset=\"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-10-1.png 591w, https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Fig.-10-1-300x120.png 300w\" sizes=\"(max-width: 591px) 100vw, 591px\" \/><\/a><\/figure>\n\n\n\n<p>The overload pattern demonstrated several C++ techniques and allows to write shorter code.&nbsp; C++17 simplifies syntax and reduces boilerplate code limiting potential errors.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Pros and Cons of std::variant polymorphism<\/strong><\/h2>\n\n\n\n<p><strong>Pros<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>value semantics,<\/li>\n\n\n\n<li>no dynamic allocation,<\/li>\n\n\n\n<li>no need for the base class,<\/li>\n\n\n\n<li>class can be unrelated,<\/li>\n\n\n\n<li>duck typing: gives extra flexibility, we can call functions from visitors with a different number of arguments, return types etc. On the contrary virtual functions need to have the same signature<\/li>\n<\/ul>\n\n\n\n<p><strong>Cons<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>need to know all types up front at compile time,<\/li>\n\n\n\n<li>hard to add new types (changing type of variant and all visitors),<\/li>\n\n\n\n<li>might waste memory,<\/li>\n\n\n\n<li>each operation requires writing separate visitors,<\/li>\n\n\n\n<li>passing parameters to <strong>std::visit<\/strong> is not as easy as with regular functions.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Summary<\/strong><\/h2>\n\n\n\n<p>Modern C++(17) extends possibilities in terms of the runtime polymorphism approach. It leverages <strong>std::variant<\/strong> and <strong>std::visit<\/strong> to obtain that behaviour and might offer better performance and value semantics.<\/p>\n\n\n\n<p>I\u2019ve shown how you can use <strong>std::visit<\/strong> with multiple variants. Such a technique might lead to various \u201cpattern matching\u201d algorithms. You have a set of types, and you want to perform some algorithm based on the currently active types. It\u2019s like doing polymorphic operations, but differently \u2013 as <strong>std::visit<\/strong> doesn\u2019t use any v-tables.<\/p>\n\n\n\n<p>***<\/p>\n\n\n\n<p>If you want more detailed information on <strong>std::visit<\/strong> and <strong>std::variant<\/strong> and its internals please visit the link:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/mpark.github.io\/programming\/2015\/07\/07\/variant-visitation\/\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >Variant visitation<\/a><\/li>\n<\/ul>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;543&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;1&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;2&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5&quot;,&quot;size&quot;:&quot;30&quot;,&quot;title&quot;:&quot;Modern approach in object-oriented design with std::variant and std::visit (C++17)&quot;,&quot;width&quot;:&quot;159&quot;,&quot;_legend&quot;:&quot;{score}\\\/5&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 159px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 24px;\">\n            5\/5    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Inheritance is one of the core concepts of object-oriented programming (OOP) languages. It is a mechanism where you can derive &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.ua\/blog\/en\/modern-approach-in-object-oriented-design-with-stdvariant-and-stdvisit-c17\/\">Continued<\/a><\/p>\n","protected":false},"author":26,"featured_media":544,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[20,90,23],"class_list":["post-543","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hard-development","tag-c17","tag-embedded","tag-stdvariant"],"acf":[],"aioseo_notices":[],"featured_media_url":"https:\/\/sii.ua\/blog\/wp-content\/uploads\/2024\/01\/Modern-approach-in-object-oriented-design-with-stdvariant-and-stdvisit-C17.jpg","category_names":["Hard development"],"_links":{"self":[{"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/posts\/543"}],"collection":[{"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/users\/26"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/comments?post=543"}],"version-history":[{"count":4,"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/posts\/543\/revisions"}],"predecessor-version":[{"id":850,"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/posts\/543\/revisions\/850"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/media\/544"}],"wp:attachment":[{"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/media?parent=543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/categories?post=543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.ua\/blog\/en\/wp-json\/wp\/v2\/tags?post=543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}