{"id":4846,"date":"2026-10-18T20:59:27","date_gmt":"2026-10-18T18:59:27","guid":{"rendered":"https:\/\/mariusbancila.ro\/blog\/?p=4846"},"modified":"2026-10-18T20:59:29","modified_gmt":"2026-10-18T18:59:29","slug":"composite-objects-and-cpp26-the-stdpolymorphic-type","status":"publish","type":"post","link":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/","title":{"rendered":"Composite objects and C++26 the std::polymorphic type"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In a <a href=\"https:\/\/mariusbancila.ro\/blog\/2026\/07\/23\/the-pimpl-idiom-and-the-cpp26-stdindirect-type\/\" target=\"_blank\" rel=\"noopener\">previous article<\/a>, I discussed the C++ <code>std::indirect<\/code> type and how it helps implementing the PImpl idiom. However, the <code>std::indirect<\/code> type was inducted in the standard alongside another type called <code>std::polymorphic<\/code>. This helps implementing composite classes, where composite means a class composed of member objects in an has-a relation, where the composite own the components which are part of its values. Examples include strategy objects, AST\/expression-tree nodes, shapes-style hierarchies, plugin-configured behavior &#8211; and you want the owning class to remain a regular, copyable value. In this article, we&#8217;ll look at how <code>std::polymorphic<\/code> can be used for this purpose.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The need for std::polymorphic<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let us consider the following class hierarchy:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">struct Event {\n  virtual std::string describe() const = 0;\n};\nstruct TemperatureReadingEvent : public Event {\n  std::string describe() const override { }\n};\nstruct SmokeDetectedEvent : public Event {\n  std::string describe() const override { }\n};\nstruct CarbonMonoxideDetectedEvent : public Event {\n  std::string describe() const override { }\n};\n\nclass EventLog {\n  ??? events_;\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We have various types of events from various sensors in a smart house and we want to store them in an event log. How do we do that? There are various options:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>Event event<\/code> &#8211; slices because a <code>TemperatureReadingEvent<\/code> assigned into it loses its temperature specific part. Doesn\u2019t even compile here since <code>Event<\/code> is abstract.<\/li>\n\n\n\n<li><code>unique_ptr&lt;Event><\/code> &#8211; preserves the dynamic type, but now your class is move-only (or you hand-write copying), <code>const<\/code> doesn\u2019t propagate, and null lurks.<\/li>\n\n\n\n<li><code>variant&lt;Event, TemperatureReadingEvent><\/code> &#8211; exhibits value semantics, but maintaince a closed set: adding a <code>CarbonMonoxideDetectedEvent<\/code> means touching the variant and every visitor.<\/li>\n\n\n\n<li><code>unique_ptr&lt;Event><\/code> and a virtual <code>clone()<\/code> function &#8211; the traditional &#8220;fix&#8221; for copying, which pushes boilerplate into every class in the hierarchy.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s try to implement the last option in the list. We start with the <code>Event<\/code> class:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;iostream>\n#include &lt;print>\n#include &lt;chrono>\n#include &lt;memory>\n#include &lt;string>\n#include &lt;vector>\n\nusing Clock = std::chrono::system_clock;\nusing TimePoint = Clock::time_point;\n\nclass Event \n{\npublic:\n   virtual ~Event() = default;\n\n   TimePoint when() const { return ts_; }\n   virtual std::string describe() const = 0;\n   virtual void apply(class HomeStatus&amp; home) const = 0;\n\n   virtual std::unique_ptr&lt;Event> clone() const = 0;\n\nprotected:\n   TimePoint ts_ = Clock::now();\n\n   \/\/ protect copying\/assigning through base pointers from slicing hazard\n   Event() = default;\n   Event(const Event&amp;) = default;\n   Event&amp; operator=(const Event&amp;) = default;\n};<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can define an overall status of the house readings derived from event as follows:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class HomeStatus \n{\npublic:\n   double lastTemperature = 0.0;\n   bool   smokeAlarm = false;\n   bool   coAlarm = false;\n   int    alertCount = 0;\n\n   bool evacuationNeeded() const { return smokeAlarm || coAlarm; }\n};<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then, we can define the actual event classes, such as the following:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class TemperatureReadingEvent : public Event \n{\n   std::string room_;\n   double celsius_;\npublic:\n   TemperatureReadingEvent(std::string room, double celsius)\n      : room_(std::move(room)), celsius_(celsius) {}\n\n   double celsius() const { return celsius_; }\n\n   std::string describe() const override {\n      return std::format(\"[{}] temperature {} C\", room_, celsius_);\n   }\n   void apply(HomeStatus&amp; home) const override {\n      home.lastTemperature = celsius_;\n   }\n\n   std::unique_ptr&lt;Event> clone() const override {\n      return std::make_unique&lt;TemperatureReadingEvent>(*this);\n   }\n};\n\nclass SmokeDetectedEvent : public Event \n{\n   std::string room_;\n   double density_;\npublic:\n   SmokeDetectedEvent(std::string room, double density)\n      : room_(std::move(room)), density_(density) {}\n\n   std::string describe() const override {\n      return std::format(\"[{}] SMOKE detected, density {} %\/m\", room_, density_);\n   }\n   void apply(HomeStatus&amp; home) const override {\n      home.smokeAlarm = true;\n      ++home.alertCount;\n   }\n\n   std::unique_ptr&lt;Event> clone() const override {\n      return std::make_unique&lt;SmokeDetectedEvent>(*this);\n   }\n};\n\nclass CarbonMonoxideDetectedEvent : public Event \n{\n   std::string room_;\n   int ppm_;\npublic:\n   CarbonMonoxideDetectedEvent(std::string room, int ppm)\n      : room_(std::move(room)), ppm_(ppm) {}\n\n   std::string describe() const override {\n      return std::format(\"[{}] CARBON MONOXIDE {} ppm\", room_, ppm_);\n   }\n\n   void apply(HomeStatus&amp; home) const override {\n      home.coAlarm = true;\n      ++home.alertCount;\n   }\n\n   std::unique_ptr&lt;Event> clone() const override {\n      return std::make_unique&lt;CarbonMonoxideDetectedEvent>(*this);\n   }\n};<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The event log that is composed of sensor reading events may look as defined below:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class EventLog {\n   std::vector&lt;std::unique_ptr&lt;Event>> events_;\npublic:\n   EventLog() = default;\n\n   \/\/ The Rule of Five because unique_ptr members delete the copy operations\n   EventLog(const EventLog&amp; other) {\n      events_.reserve(other.events_.size());\n      for (const auto&amp; e : other.events_)\n         events_.push_back(e->clone());\n   }\n\n   EventLog&amp; operator=(const EventLog&amp; other) {\n      if (this != &amp;other) {\n         std::vector&lt;std::unique_ptr&lt;Event>> tmp;    \/\/ copy-then-commit\n         tmp.reserve(other.events_.size());          \/\/ for exception safety\n         for (const auto&amp; e : other.events_)\n            tmp.push_back(e->clone());\n         events_ = std::move(tmp);\n      }\n      return *this;\n   }\n\n   EventLog(EventLog&amp;&amp;) noexcept = default;\n   EventLog&amp; operator=(EventLog&amp;&amp;) noexcept = default;\n   ~EventLog() = default;\n\n   template &lt;typename E, typename... Args>\n   void record(Args&amp;&amp;... args) {\n      events_.push_back(std::make_unique&lt;E>(std::forward&lt;Args>(args)...));\n   }\n\n   HomeStatus replay() const {\n      HomeStatus home;\n      for (const auto&amp; e : events_)\n         e->apply(home);\n      return home;\n   }\n\n   void print() const {\n      for (const auto&amp; e : events_)\n         std::println(\"  {}\", e->describe());\n   }\n\n   std::size_t size() const { return events_.size(); }\n};<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Putting it all together, we can use these classes as shown in the following snippet:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int main() \n{\n   EventLog live;\n   live.record&lt;TemperatureReadingEvent>(\"kitchen\", 21.5);\n   live.record&lt;TemperatureReadingEvent>(\"kitchen\", 48.0);\n   live.record&lt;CarbonMonoxideDetectedEvent>(\"garage\", 120);\n\n   std::println(\"Live log has {} events\", live.size());\n   live.print();\n\n   HomeStatus now = live.replay();\n   std::println(\"Current home status: lastTemp={}, smoke={}, co={}, alerts={}, evacuate={}\", \n      now.lastTemperature, \n      now.smokeAlarm, \n      now.coAlarm, \n      now.alertCount, \n      now.evacuationNeeded());\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There are a series of problems with this implementation:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The owner loses copyability. <code>unique_ptr<\/code> is move-only, so the composite&#8217;s copy operations are deleted. Getting them back means hand-writing a copy constructor and copy assignment that clone every component, which means the Rule of Zero is defeated.<\/li>\n\n\n\n<li>The clone() function affects the whole hierarchy, as every derived class must implement it. But on the other hand, the compiler can&#8217;t enforce it, which means a forgotten override doesn&#8217;t fail to build; it silently produces sliced copies.<\/li>\n\n\n\n<li>Infrastructure leaks into the interface. The function <code>clone()<\/code> says nothing about the type. It is a copying workaround sitting in the public API next to the methods that actually define the type.<\/li>\n\n\n\n<li><code>const<\/code> doesn\u2019t propagate. Through a <code>const unique_ptr<\/code>, the pointee is still mutable (because <code>const<\/code> protects the pointer, not the component).<\/li>\n\n\n\n<li>null is representable. Nothing prevents a null pointer from entering the container or member, so every dereference carries an implicit precondition the type system doesn&#8217;t express.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Enter std::polymorphic<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Using the C++26 type <code>std::polymorphic<\/code>, we no longer need to use a cloning mechanism to allow components to behave polymorphically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the given example, we can simplify the <code>Event<\/code> class to the following:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Event \n{\npublic:\n   virtual ~Event() = default;\n\n   TimePoint when() const { return ts_; }\n   virtual std::string describe() const = 0;\n   virtual void apply(class HomeStatus&amp; home) const = 0;\nprotected:\n   TimePoint ts_ = Clock::now();\n};<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Consequently, the derived classes change as shown here:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class TemperatureReadingEvent : public Event \n{\n   std::string room_;\n   double celsius_;\npublic:\n   TemperatureReadingEvent(std::string room, double celsius)\n      : room_(std::move(room)), celsius_(celsius) {}\n\n   double celsius() const { return celsius_; }\n\n   std::string describe() const override {\n      return std::format(\"[{}] temperature {} C\", room_, celsius_);\n   }\n\n   void apply(HomeStatus&amp; home) const override {\n      home.lastTemperature = celsius_;\n   }\n};\n\nclass SmokeDetectedEvent : public Event\n{\n   std::string room_;\n   double density_;\npublic:\n   SmokeDetectedEvent(std::string room, double density)\n      : room_(std::move(room)), density_(density) {}\n\n   std::string describe() const override {\n      return std::format(\"[{}] SMOKE detected, density {} %\/m\", room_, density_);\n   }\n\n   void apply(HomeStatus&amp; home) const override {\n      home.smokeAlarm = true;\n      ++home.alertCount;\n   }\n};\n\nclass CarbonMonoxideDetectedEvent : public Event \n{\n   std::string room_;\n   int ppm_;\npublic:\n   CarbonMonoxideDetectedEvent(std::string room, int ppm)\n      : room_(std::move(room)), ppm_(ppm) {}\n\n   std::string describe() const override {\n      return std::format(\"[{}] CARBON MONOXIDE {} ppm\", room_, ppm_);\n   }\n   void apply(HomeStatus&amp; home) const override {\n      home.coAlarm = true;\n      ++home.alertCount;\n   }\n};<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>EventLog<\/code> class no longer need to adhere to the Rule of Five and implement all the special member functions. In fact, it can follow the Rule of Zero and implement none.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class EventLog {\n   std::vector&lt;std::polymorphic&lt;Event>> events_;\npublic:\n   template &lt;typename E, typename... Args>\n   void record(Args&amp;&amp;... args) {\n      events_.emplace_back(std::in_place_type&lt;E>, std::forward&lt;Args>(args)...);\n   }\n\n   HomeStatus replay() const {\n      HomeStatus home;\n      for (const auto&amp; e : events_)\n         e->apply(home);\n      return home;\n   }\n\n   void print() const {\n      for (const auto&amp; e : events_)\n         std::cout &lt;&lt; \"  \" &lt;&lt; e->describe() &lt;&lt; \"\\n\";\n   }\n\n   std::size_t size() const { return events_.size(); }\n};<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The client code does not change. The <code>main()<\/code> function from the previous section does not change. However, the entire code has become simpler. <code>std::polymorphic<\/code> solves a series of issues:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The owner is copyable again: <code>std::polymorphic<\/code> is itself copyable, performing a correct deep copy of the owned object. The composite&#8217;s copy operations are compiler-generated, which means they follow the Rule of Zero.<\/li>\n\n\n\n<li>No <code>clone()<\/code> protocol: the copy operation is type-erased and captured at construction, when the concrete type is statically known. The hierarchy needs no <code>clone()<\/code> function, and there&#8217;s nothing for a derived class to forget.<\/li>\n\n\n\n<li>Clean interfaces: with no <code>clone()<\/code> to declare, the base class contains only domain methods.<\/li>\n\n\n\n<li><code>const<\/code> propagates: through a <code>const std::polymorphic<\/code>, access to the owned object is const. Deep const is enforced by the type system, not by convention.<\/li>\n\n\n\n<li>Never null: a <code>std::polymorphic<\/code> object always owns a value; there is no null state to construct. The only empty state is moved-from, explicitly detectable via <code>valueless_after_move()<\/code>, so every dereference elsewhere is valid by invariant.<\/li>\n<\/ul>\n\n\n\n<p class=\"advgb-dyn-b2b08962 wp-block-paragraph\">In short, the <code>std::polymorphic<\/code> component behaves like a regular member again: copyable, deeply const, always present, while keeping its dynamic type.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You may ask, when you copy a <code>std::polymorphic&lt;Event><\/code> that holds a <code>TemperatureReadingEvent<\/code>, how does it know to copy a <code>TemperatureReadingEvent<\/code> without a virtual <code>clone()<\/code>? The answer is type erasure of the copy operation, captured at construction time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually, <code>std::polymorphic&lt;Event><\/code> stores its object behind a small internal control structure. When you construct it with <code>std::in_place_type&lt;TemperatureReadingEvent><\/code>, the constructor, which at that moment statically knows the type is <code>TemperatureReadingEvent<\/code>, instantiates and stores operations equivalent to the following:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">copy    = [](const Event&amp; e) { return new TemperatureReadingEvent(static_cast&lt;const TemperatureReadingEvent&amp;>(e)); };\ndestroy = [](Event* e)       { delete static_cast&lt;Event*>(e); };<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Later, <code>std::polymorphic<\/code>&#8216;s copy constructor just invokes the stored operation. It&#8217;s the same technique <code>std::function<\/code> and <code>std::any<\/code> use, applied to the copy constructor. This also means <code>Event<\/code> doesn&#8217;t even need a virtual destructor for <code>std::polymorphic<\/code> to destroy it correctly. The erased destroy operation already knows the concrete type. That doesn&#8217;t mean you should remove the the virtual destructor, as other code may still delete through base pointers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The rest of the interface mirrors the <code>std::indirect<\/code> type:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>operator*<\/code> \/ <code>operator-><\/code> give you <code>Event&amp;<\/code> \/ <code>Event*<\/code>, const-propagated (through a <code>const std::polymorphic<\/code> you get <code>const Event&amp;<\/code>).<\/li>\n\n\n\n<li>Never null, except the moved-from state, queryable with <code>valueless_after_move()<\/code>.<\/li>\n\n\n\n<li>Allocator-aware, and unlike <code>std::indirect<\/code>, implementations are permitted a small-buffer optimization, storing small derived objects inline without heap allocation.<\/li>\n\n\n\n<li>There&#8217;s no way to ask &#8220;which derived type is inside?&#8221;. It doesn&#8217;t feature a <code>type()<\/code> function like <code>std::any<\/code> has. If you need that, use <code>dynamic_cast<\/code> on <code>*p<\/code> like you would with any base reference. The design intent is that you interact through the base interface.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use <code>std::polymorphic<\/code> when a class owns a component whose dynamic type varies at runtime and that component is part of the owner&#8217;s value: copying the owner should copy it, <code>const<\/code> should propagate to it, and it should never be absent. Typical cases are strategy members, heterogeneous collections like event logs, and recursive structures like expression trees.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the type is fixed, use <code>std::indirect<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the set of alternatives is closed, use <code>std::variant<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the object is a shared identity rather than a value, stick with smart pointers.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">See also<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here is a list of additional readings on the subject:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/en.cppreference.com\/cpp\/memory\/polymorphic\" target=\"_blank\" rel=\"noopener\">std::polymorphic<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/towardsdev.com\/std-polymorphic-cpp26-value-semantics-de1f0abdcbfa\" target=\"_blank\" rel=\"noopener\">Copying Polymorphic Types: How C++26 simplifies value semantics<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.sandordargo.com\/blog\/2026\/08\/19\/cpp26-polymorphic\" target=\"_blank\" rel=\"noopener\">C++26: std::polymorphic<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a previous article, I discussed the C++ std::indirect type and how it helps implementing the PImpl idiom. However, the std::indirect type was inducted in the standard alongside another type called std::polymorphic. This helps implementing composite classes, where composite means a class composed of member objects in an has-a relation, where the composite own the &#8230; <a title=\"Composite objects and C++26 the std::polymorphic type\" class=\"read-more\" href=\"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/\" aria-label=\"Read more about Composite objects and C++26 the std::polymorphic type\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[7],"tags":[451,647],"class_list":["post-4846","post","type-post","status-publish","format-standard","hentry","category-c","tag-c","tag-c26"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Marius Bancila\"\/>\n\t<meta name=\"keywords\" content=\"c++,c++26\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Marius Bancila&#039;s Blog | About code. Mostly on C++\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Composite objects and C++26 the std::polymorphic type\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-10-18T18:59:27+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-10-18T18:59:29+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Composite objects and C++26 the std::polymorphic type\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#article\",\"name\":\"Composite objects and C++26 the std::polymorphic type\",\"headline\":\"Composite objects and C++26 the std::polymorphic type\",\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"},\"datePublished\":\"2026-10-18T20:59:27+02:00\",\"dateModified\":\"2026-10-18T20:59:29+02:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#webpage\"},\"articleSection\":\"C++, C++, C++26\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"position\":2,\"name\":\"IT\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"position\":3,\"name\":\"Software\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"position\":4,\"name\":\"C++\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#listItem\",\"name\":\"Composite objects and C++26 the std::polymorphic type\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#listItem\",\"position\":5,\"name\":\"Composite objects and C++26 the std::polymorphic type\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/\",\"name\":\"Marius Bancila\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Marius Bancila\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#webpage\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/\",\"name\":\"Composite objects and C++26 the std::polymorphic type\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/10\\\/18\\\/composite-objects-and-cpp26-the-stdpolymorphic-type\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"datePublished\":\"2026-10-18T20:59:27+02:00\",\"dateModified\":\"2026-10-18T20:59:29+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Composite objects and C++26 the std::polymorphic type","description":"","canonical_url":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/","robots":"max-image-preview:large","keywords":"c++,c++26","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#article","name":"Composite objects and C++26 the std::polymorphic type","headline":"Composite objects and C++26 the std::polymorphic type","author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"},"datePublished":"2026-10-18T20:59:27+02:00","dateModified":"2026-10-18T20:59:29+02:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#webpage"},"isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#webpage"},"articleSection":"C++, C++, C++26"},{"@type":"BreadcrumbList","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","position":1,"name":"Home","item":"https:\/\/mariusbancila.ro\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","position":2,"name":"IT","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","position":3,"name":"Software","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","position":4,"name":"C++","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#listItem","name":"Composite objects and C++26 the std::polymorphic type"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#listItem","position":5,"name":"Composite objects and C++26 the std::polymorphic type","previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"}}]},{"@type":"Organization","@id":"https:\/\/mariusbancila.ro\/blog\/#organization","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","url":"https:\/\/mariusbancila.ro\/blog\/"},{"@type":"Person","@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author","url":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/","name":"Marius Bancila","image":{"@type":"ImageObject","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g","width":96,"height":96,"caption":"Marius Bancila"}},{"@type":"WebPage","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#webpage","url":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/","name":"Composite objects and C++26 the std::polymorphic type","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/#breadcrumblist"},"author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"creator":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"datePublished":"2026-10-18T20:59:27+02:00","dateModified":"2026-10-18T20:59:29+02:00"},{"@type":"WebSite","@id":"https:\/\/mariusbancila.ro\/blog\/#website","url":"https:\/\/mariusbancila.ro\/blog\/","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","inLanguage":"en-US","publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Marius Bancila's Blog | About code. Mostly on C++","og:type":"article","og:title":"Composite objects and C++26 the std::polymorphic type","og:url":"https:\/\/mariusbancila.ro\/blog\/2026\/10\/18\/composite-objects-and-cpp26-the-stdpolymorphic-type\/","article:published_time":"2026-10-18T18:59:27+00:00","article:modified_time":"2026-10-18T18:59:29+00:00","twitter:card":"summary","twitter:title":"Composite objects and C++26 the std::polymorphic type"},"aioseo_meta_data":{"post_id":"4846","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-10-18 15:46:48","updated":"2026-10-18 18:59:29","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"author_meta":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"featured_img":null,"jetpack_publicize_connections":[],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"uagb_comment_info":0,"uagb_excerpt":"In a previous article, I discussed the C++ std::indirect type and how it helps implementing the PImpl idiom. However, the std::indirect type was inducted in the standard alongside another type called std::polymorphic. This helps implementing composite classes, where composite means a class composed of member objects in an has-a relation, where the composite own the&hellip;","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++26<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">C++26<\/span>"]}},"comment_count":"0","relative_dates":{"created":"Posted 2 hours ago","modified":"Updated 2 hours ago"},"absolute_dates":{"created":"Posted on October 18, 2026","modified":"Updated on October 18, 2026"},"absolute_dates_time":{"created":"Posted on October 18, 2026 8:59 pm","modified":"Updated on October 18, 2026 8:59 pm"},"featured_img_caption":"","series_order":"","jetpack_shortlink":"https:\/\/wp.me\/pYNdv-1ga","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4846","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/comments?post=4846"}],"version-history":[{"count":8,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4846\/revisions"}],"predecessor-version":[{"id":4854,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4846\/revisions\/4854"}],"wp:attachment":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/media?parent=4846"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/categories?post=4846"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/tags?post=4846"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}