{"id":110420,"date":"2024-10-25T07:00:00","date_gmt":"2024-10-25T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110420"},"modified":"2024-10-25T10:33:22","modified_gmt":"2024-10-25T17:33:22","slug":"20241025-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241025-00\/?p=110420","title":{"rendered":"How can I explicitly specialize a templated C++ constructor, follow-up notes"},"content":{"rendered":"<p>A brief follow-up on <a title=\"How can I explicitly specialize a templated C++ constructor\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241011-00\/?p=110365\"> How can I explicitly specialize a templated C++ constructor?<\/a>, which pedantically should probably have been titled something like &#8220;How can I explicitly <i>instantiate<\/i> a templated C++ constructor?&#8221; but what&#8217;s done is done.<\/p>\n<p>Our solution was to use the <code>in_<wbr \/>place_<wbr \/>type<\/code> type-holder class to specify what type of object the object manager should contain:<\/p>\n<pre>struct ObjectManager\r\n{\r\n    \/\/ Concrete should derive from CommonBase\r\n    template&lt;typename <span style=\"border: solid 1px currentcolor;\">Concrete<\/span>, typename...Args&gt;\r\n    ObjectManager(int reason,\r\n        <span style=\"border: solid 1px currentcolor;\">std::in_place_type_t&lt;Concrete&gt;,<\/span>\r\n        Args&amp;&amp;...args) :\r\n        m_base(std::make_unique&lt;Concrete&gt;(\r\n                *this, std::forward&lt;Args&gt;(args)...))\r\n    {\r\n        m_base-&gt;initialize(reason);\r\n    }\r\n\r\n    std::unique_ptr&lt;CommonBase&gt; m_base;\r\n};\r\n<\/pre>\n<p>We could also apply the <a title=\"It's great that you provide operator overloads, but it's also nice to have names\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230605-00\/?p=108289\"> &#8220;give it a name&#8221; principle<\/a> to the problem and offer a factory method, which is easier to instantiate with an explicit type.<\/p>\n<pre>struct ObjectManager\r\n{\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">template&lt;typename Concrete, typename...Args&gt;        <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">static ObjectManager make(int reason, Args&amp;&amp;...args)<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    return ObjectManager(reason,                    <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        std::in_place_type&lt;Concrete&gt;                <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        std::forward&lt;Args&gt;(args)...));              <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                   <\/span>\r\n\r\n    \u27e6 ... as before ... \u27e7\r\n};\r\n\r\n\/\/ Example usage:\r\nauto manager = ObjectManager::make&lt;Derived&gt;(9, 42);\r\n<\/pre>\n<p>Note that our solution still uses a type tag parameter (in this case, <code>in_<wbr \/>place_<wbr \/>type<\/code>). This is unavoidable because the <code>Object\u00adManager<\/code> constructor uses a reference to itself. This is a Catch-22 we ran into when we tried to <a title=\"Constructing nodes of a hand-made linked list, how hard can it be?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240819-00\/?p=110145\"> construct nodes of a hand-made linked list<\/a>. Consider this alternative:<\/p>\n<pre>struct ObjectManager\r\n{\r\n    template&lt;typename Concrete, typename...Args&gt;\r\n    static ObjectManager make(int reason, Args&amp;&amp;...args)\r\n    {\r\n        \/\/ Code in italics is wrong\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\"><i>ObjectManager manager(reason,                  <\/i><\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\"><i>    std::make_unique&lt;Concrete&gt;(                <\/i><\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\"><i>        manager, std::forward&lt;Args&gt;(args)...));<\/i><\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\"><i>return manager;                                <\/i><\/span>\r\n    }\r\n\r\n    template&lt;typename Trait, typename...Args&gt;\r\n    ObjectManager(int reason, <span style=\"border: solid 1px currentcolor;\">std::unique_ptr&lt;CommonBase&gt; base<\/span>) :\r\n        m_base(std::move(base))\r\n    {\r\n        m_base-&gt;initialize(reason);\r\n    }\r\n\r\n    std::unique_ptr&lt;CommonBase&gt; m_base;\r\n};\r\n<\/pre>\n<p>This version creates an <code>Object\u00adManager<\/code> that handed out a reference to itself (<code>*this<\/code>), and then moves that <code>Object\u00adManager<\/code> to the return value, thereby changing its address and invalidating the reference.<\/p>\n<p>The above code is eligible for named return value optimization (NRVO), in which case the <code>manager<\/code> object can be constructed directly in the return value slot. However, NRVO is not a mandatory optimization, so a compiler is permitted to construct the <code>manager<\/code> separately and then move it\u00b9 to the return value slot when the function exits.<\/p>\n<p>Return value optimization (RVO, formally called <i>copy elision<\/i>) is guaranteed in the case where you return a freshly-constructed object, which is why our initial version takes the form <code>return ObjectManager(...)<\/code>.<\/p>\n<p>\u00b9 Even though you didn&#8217;t explicitly write <code>std::move(manager)<\/code>, the compiler is required to move it. In fact, if you write <code>return std::move(manager);<\/code>, <a title=\"On harmful overuse of std::move\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20231124-00\/?p=109059\"> you have inadvertently defeated the <code>std::move<\/code> optimization<\/a>!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You can use a maker function, but that doesn&#8217;t let you escape the problem.<\/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-110420","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You can use a maker function, but that doesn&#8217;t let you escape the problem.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110420","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=110420"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110420\/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=110420"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110420"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110420"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}