{"id":110365,"date":"2024-10-11T07:00:00","date_gmt":"2024-10-11T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110365"},"modified":"2024-10-11T13:50:29","modified_gmt":"2024-10-11T20:50:29","slug":"20241011-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241011-00\/?p=110365","title":{"rendered":"How can I explicitly specialize a templated C++ constructor?"},"content":{"rendered":"<p>C++ allows constructors to be templated, but there is no syntax for explicitly specializing the constructor. Here&#8217;s a rather artificial example:<\/p>\n<pre>\/\/ Assume derived classes by convention have a constructor\r\n\/\/ whose first parameter is an ObjectManager&amp;.\r\nstruct CommonBase\r\n{\r\n    virtual ~CommonBase(){}\r\n    virtual void initialize(int reason) = 0;\r\n};\r\n\r\nstruct ObjectManager\r\n{\r\n    \/\/ Concrete should derive from CommonBase\r\n    template&lt;typename Concrete, typename...Args&gt;\r\n    ObjectManager(int reason, 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>The idea here is that you have some type, and you want to templatize the constructor. It is legal to have a templated constructor, but there is no way to explicitly specialize a constructor.<\/p>\n<pre>struct Widget : CommonBase\r\n{\r\n    Widget(int param);\r\n    \u27e6 ... \u27e7\r\n};\r\n\r\n\/\/ This is not allowed\u00b9\r\nauto manager = ObjectManager::ObjectManager&lt;Widget&gt;(42);\r\n<\/pre>\n<p>So how do you tell the constructor, &#8220;I want you to use this type for <code>Concrete<\/code>?&#8221;<\/p>\n<p>Your only option is type inference, so you&#8217;ll have to make it inferrable from a parameter.<\/p>\n<p>Enter <code>std::<wbr \/>in_<wbr \/>place_<wbr \/>type<\/code> and friends.<\/p>\n<p>We start with <code>std::<wbr \/>in_<wbr \/>place_<wbr \/>type_t<\/code>, which is an empty type that takes a single type as a template parameter. You can use this as a dummy parameter and deduce the template type parameter from it.<\/p>\n<pre>struct ObjectManager\r\n{\r\n    \/\/ Concrete should derive from CommonBase\r\n    template&lt;typename Concrete, 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\r\n\/\/ Example usage:\r\nauto manager = ObjectManager(9, std::in_place_type_t&lt;Derived&gt;{}, 42);\r\n<\/pre>\n<p>The <code>in_<wbr \/>place_<wbr \/>type_t<\/code> is an empty class that is default-constructible. As a convenience, the standard library also defines a premade value:<\/p>\n<pre>template&lt;T&gt;\r\ninline constexpr std::in_place_type_t in_place_type{};\r\n<\/pre>\n<p>Which lets you simplify the usage to<\/p>\n<pre>auto manager = ObjectManager(9, std::in_place_type&lt;Derived&gt;, 42);\r\n<\/pre>\n<p>Note that there is no member type <code>type<\/code> inside the <code>std::<wbr \/>in_<wbr \/>place_<wbr \/>type_t<\/code>, so you have to use deduction to pull it out. You can&#8217;t say<\/p>\n<pre>    \/\/ Concrete should derive from CommonBase\r\n    template&lt;typename <span style=\"border: solid 1px currentcolor;\">Trait<\/span>, typename...Args&gt;\r\n    ObjectManager(int reason,\r\n        <span style=\"border: solid 1px currentcolor;\">Trait<\/span>,\r\n        Args&amp;&amp;...args) :\r\n        m_base(std::make_unique&lt;<span style=\"border: solid 1px currentcolor;\">typename Trait::type<\/span>&gt;(\r\n                *this, std::forward&lt;Args&gt;(args)...))\r\n    {\r\n        m_base-&gt;initialize(reason);\r\n    }\r\n<\/pre>\n<p>You might be tempted to use <code>std::<wbr \/>type_identity<\/code>\u00b2 as the type holder:<\/p>\n<pre>    \/\/ Concrete should derive from CommonBase\r\n    template&lt;typename Concrete, typename...Args&gt;\r\n    ObjectManager(int reason,\r\n        std::<span style=\"border: solid 1px currentcolor;\">type_identity<\/span>&lt;Concrete&gt;,\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<\/pre>\n<p>but that is not allowed.<\/p>\n<p>According to the C++ standard, <code>std::<wbr \/>type_<wbr \/>identity<\/code> is a Cpp17TransformationTrait, and <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/meta.rqmts\"> [meta.rqmts]<\/a> spells out the requirements of various trait types in the standard library.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Trait<\/th>\n<th>Constructible?<\/th>\n<th>Copyable?<\/th>\n<th>Special member<\/th>\n<\/tr>\n<tr>\n<td>Cpp17UnaryTypeTrait<\/td>\n<td align=\"center\">Yes<\/td>\n<td align=\"center\">Yes<\/td>\n<td align=\"center\"><code>value<\/code><\/td>\n<\/tr>\n<tr>\n<td>Cpp17BinaryTypeTrait<\/td>\n<td align=\"center\">Yes<\/td>\n<td align=\"center\">Yes<\/td>\n<td align=\"center\"><code>value<\/code><\/td>\n<\/tr>\n<tr>\n<td>Cpp17TransformationTrait<\/td>\n<td align=\"center\">No<\/td>\n<td align=\"center\">No<\/td>\n<td align=\"center\"><code>type<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Since a Cpp17TransformationTrait is not constructible, and the language does not provide any pre-made instances, there is no legal way of gaining access to an instance of a Cpp17TransformationTrait. An implemention would be within its rights to define <code>type_<wbr \/>identity<\/code> as<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct type_identity\r\n{\r\n    using type = T;\r\n\r\n    \/\/ not constructible\r\n    type_identity() = delete;\r\n\r\n    \/\/ not copyable\r\n    type_identity(type_identity const&amp;) = delete;\r\n}\r\n<\/pre>\n<p>\u00b9 Another place you cannot specialize a templated function is operator overloading.<\/p>\n<pre>struct ObjectMaker\r\n{\r\n    ObjectMaker(std::string name) : m_name(std::move(name)) {}\r\n\r\n    template&lt;typename Concrete&gt;\r\n    Concrete operator()() { return Concrete(m_name); }\r\n\r\n    std::string m_name;\r\n};\r\n\r\nvoid sample()\r\n{\r\n    ObjectMaker maker(\"adam\");\r\n\r\n    \/\/ You can't do this\r\n    auto thing1 = maker&lt;Thing1&gt;();\r\n    auto thing2 = maker&lt;Thing2&gt;();\r\n}\r\n<\/pre>\n<p>You have to use more cumbersome syntax to specialize the overloaded operator:<\/p>\n<pre>void sample()\r\n{\r\n    ObjectMaker maker(\"adam\");\r\n\r\n    \/\/ You have to write it like this\r\n    auto thing1 = maker.operator()&lt;Thing1&gt;();\r\n    auto thing2 = maker.operator()&lt;Thing2&gt;();\r\n}\r\n<\/pre>\n<p>It&#8217;s cumbersome, but at least it&#8217;s possible.<\/p>\n<p>But if you&#8217;re going to do that, you may as well <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\"> give it a name<\/a>:<\/p>\n<pre>struct ObjectMaker\r\n{\r\n    ObjectMaker(std::string name) : m_name(std::move(name)) {}\r\n\r\n    template&lt;typename Concrete&gt;\r\n    Concrete <span style=\"border: solid 1px currentcolor;\">make<\/span>() { return Concrete(m_name); }\r\n\r\n    std::string m_name;\r\n};\r\n\r\nvoid sample()\r\n{\r\n    ObjectMaker maker(\"adam\");\r\n\r\n    auto thing1 = maker<span style=\"border: solid 1px currentcolor;\">.make<\/span>&lt;Thing1&gt;();\r\n    auto thing2 = maker<span style=\"border: solid 1px currentcolor;\">.make<\/span>&lt;Thing2&gt;();\r\n}\r\n<\/pre>\n<p>\u00b2 For further reading: <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240607-00\/?p=109865\"> What&#8217;s the deal with <code>std::<wbr \/>type_identity<\/code><\/a>?&#8221;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Looking for a C++ tag type for representing another type.<\/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-110365","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Looking for a C++ tag type for representing another type.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110365","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=110365"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110365\/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=110365"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110365"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110365"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}