{"id":110773,"date":"2025-01-16T07:00:00","date_gmt":"2025-01-16T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110773"},"modified":"2025-01-16T10:11:18","modified_gmt":"2025-01-16T18:11:18","slug":"20250116-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250116-00\/?p=110773","title":{"rendered":"In a C++ class template specialization, how can I call the unspecialized version of a method?"},"content":{"rendered":"<p>Suppose you have some class<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct Producer\r\n{\r\n    T Produce();\r\n};\r\n<\/pre>\n<p>and suppose you want to specialize it for one particular type, but you want your specialization to call the unspecialized version too.<\/p>\n<pre>template&lt;&gt;\r\nstruct Producer&lt;void&gt;\r\n{\r\n    void Produce()\r\n    {\r\n        Log(\"Producing void!\");\r\n        \/\/ I want to call Producer&lt;T&gt;::Produce()\r\n        \/\/ as if this specialization didn't exist\r\n        Producer::Produce(); \/\/ this doesn't work\r\n    }\r\n};\r\n<\/pre>\n<p>The idea is that you want the specialized <code>Producer&lt;void&gt;::<wbr \/>Produce()<\/code> method to behave the same as an unspecialized <code>Producer&lt;void&gt;::<wbr \/>Produce()<\/code>, but do a little extra logging.<\/p>\n<p>What is the magic syntax for calling the unspecialized version of a template method?<\/p>\n<p>This is a trick question. There is no magic syntax for this. There is no way to talk about a template that doesn&#8217;t exist but &#8220;would exist&#8221; if a specialization were not present. Template specialization is not derivation. Specializing a class template means &#8220;For this case, I want the class to look like this.&#8221; It is not an override or overload or derivation; it is a definition. The definition of <code>Producer&lt;void&gt;<\/code> is the class you defined. There is no syntax for saying, &#8220;Give me <code>Producer&lt;void&gt;<\/code> as it would have been in the absence of this specialization,&#8221; and your redefinition does not implicitly derive from anything.<\/p>\n<p>There are number of possible workarounds to this.<\/p>\n<p>Probably the most common one is to move everything to a base class, have the primary template derive from the base class, and then override the method in the specialization.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct ProducerBase\r\n{\r\n    T Produce();\r\n};\r\n\r\ntemplate&lt;typename T&gt;\r\nstruct Producer : ProducerBase&lt;T&gt;\r\n{\r\n};\r\n\r\ntemplate&lt;&gt;\r\nstruct Producer&lt;void&gt; : ProducerBase&lt;void&gt;\r\n{\r\n    <a title=\"Why can\u2019t I find the injected name of a templated class\u2019s templated base class?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240517-00\/?p=109774\">using ProducerBase = typename Producer::ProducerBase;<\/a>\r\n\r\n    void Produce()\r\n    {\r\n        Log(\"Producing void!\");\r\n        ProducerBase::Produce();\r\n    }\r\n};\r\n<\/pre>\n<p>You might be in a case where the <code>Producer<\/code> class comes from a library you do not control. In that case, you can still follow the pattern, but using the library class as the base class.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct Producer\r\n{\r\n    T Produce();\r\n};\r\n\r\ntemplate&lt;typename T&gt;\r\nstruct MyProducer : Producer&lt;T&gt;\r\n{\r\n};\r\n\r\ntemplate&lt;&gt;\r\nstruct MyProducer&lt;void&gt; : Producer&lt;void&gt;\r\n{\r\n    using Producer = typename MyProducer::Producer;\r\n\r\n    void Produce()\r\n    {\r\n        Log(\"Producing void!\");\r\n        Producer::Produce();\r\n    }\r\n};\r\n\r\n\/\/ and your code uses MyProducer everywhere\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>You can&#8217;t talk about things that might exist, so instead talk about things that do.<\/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-110773","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You can&#8217;t talk about things that might exist, so instead talk about things that do.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110773","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=110773"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110773\/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=110773"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110773"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110773"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}