{"id":110942,"date":"2025-03-06T07:00:00","date_gmt":"2025-03-06T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110942"},"modified":"2025-03-06T09:53:51","modified_gmt":"2025-03-06T17:53:51","slug":"20250306-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250306-00\/?p=110942","title":{"rendered":"How can I choose a different C++ constructor at runtime?"},"content":{"rendered":"<p>Suppose you have a class with two constructors.<\/p>\n<pre>struct WidgetBase\r\n{\r\n    \/\/ local mode\r\n    WidgetBase();\r\n\r\n    \/\/ remote mode\r\n    WidgetBase(std::string const&amp; server);\r\n\r\n    \/\/ The mutex makes this non-copyable, non-movable\r\n    std::mutex m_mutex;\r\n};\r\n\r\nstruct WidgetOptions\r\n{\r\n    \u27e6 random stuff \u27e7\r\n};\r\n\r\nstruct Widget : WidgetBase\r\n{\r\n    Widget(WidgetOptions const&amp; options) :\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ This doesn't work                <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">CanBeLocal(options)                 <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    ? WidgetBase()                  <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">    : WidgetBase(GetServer(options))<\/span>\r\n    {}\r\n\r\n    static bool CanBeLocal(WidgetOptions const&amp;);\r\n    static std::string GetServer(WidgetOptions const&amp;);\r\n};\r\n<\/pre>\n<p>We want to use the base class&#8217;s local constructor if the options are compatible with a local Widget. Otherwise, we have to create a remote Widget. But you can&#8217;t choose a base class constructor at runtime. Your constructor has to call the base class constructor somehow, and by that point the decision has already been made.<\/p>\n<p>You might try using the ternary operator.<\/p>\n<pre>    Widget(WidgetOptions const&amp; options) :\r\n        WidgetBase(\r\n            <span style=\"border: solid 1px currentcolor; border-bottom: none;\">CanBeLocal(options)                 <\/span>\r\n            <span style=\"border: 1px currentcolor; border-style: none solid;\">    ? WidgetBase()                  <\/span>\r\n            <span style=\"border: solid 1px currentcolor; border-top: none;\">    : WidgetBase(GetServer(options))<\/span>)\r\n    {}\r\n<\/pre>\n<p>But this doesn&#8217;t work because it invokes the copy and\/or move constructor: The ternary operator produces a <code>WidgetBase<\/code> by one means or another, and then we have to copy\/move the temporary into the base class <code>WidgetBase<\/code> object.<\/p>\n<p>The secret, <a title=\"How do I put a non-copyable, non-movable, non-constructible object into a std::optional?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241115-00\/?p=110527\"> once again<\/a>, is to take advantage of copy elision.<\/p>\n<pre>struct Widget : WidgetBase\r\n{\r\n    Widget(WidgetOptions const&amp; options) :\r\n        WidgetBase(<span style=\"border: solid 1px currentcolor;\">ChooseWidgetBase(options)<\/span>)\r\n    {}\r\n\r\n    static bool CanBeLocal(WidgetOptions const&amp;);\r\n    static std::string GetServer(WidgetOptions const&amp;);\r\n\r\nprivate:\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">static WidgetBase ChooseWidgetBase(           <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    WidgetOptions const&amp; options)             <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">{                                             <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    if (CanBeLocal(options)) {                <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        return WidgetBase();                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    } else {                                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        return WidgetBase(GetServer(options));<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    }                                         <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                             <\/span>\r\n};\r\n<\/pre>\n<p>This looks the same as the ternary, just moved out of line, but it&#8217;s subtly different.<\/p>\n<p>The difference is that all of the <code>return<\/code> statements use one of the magic copy elision forms: <code>return WidgetBase(\u27e6...\u27e7)<\/code>. This allows the compiler to construct the <code>Widget\u00adBase<\/code> object directly into the return value, and when called from the <code>Widget<\/code> constructor, the return value is the <code>WidgetBase<\/code> base class object.<\/p>\n<p>If you like throwing everything inline, you can use a lambda to put the helper directly into the base class constructor arguments.<\/p>\n<pre>    Widget(WidgetOptions const&amp; options) :\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">WidgetBase([&amp;] {                              <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    if (CanBeLocal(options)) {                <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">        return WidgetBase();                  <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    } else {                                  <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">        return WidgetBase(GetServer(options));<\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    }                                         <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">}())                                          <\/span>\r\n    {}\r\n<\/pre>\n<p><b>Bonus chatter<\/b>: The problem with the ternary is that the ternary expression is not a copy elision candidate. The rule for ternary expressions is that <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/expr.cond#7.1\"> the result is <i>initialized from<\/i> the branch of the ternary that is selected<\/a>. The value from the branch is copied\/moved into the expression result, and it is the result that is constructed in place.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Make somebody else do it, and then use copy elision.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-110942","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Make somebody else do it, and then use copy elision.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110942","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=110942"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110942\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=110942"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110942"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110942"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}