{"id":111063,"date":"2025-04-10T07:00:00","date_gmt":"2025-04-10T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111063"},"modified":"2025-04-10T08:45:54","modified_gmt":"2025-04-10T15:45:54","slug":"20250410-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250410-00\/?p=111063","title":{"rendered":"Function overloading is more flexible (and more convenient) than template function specialization"},"content":{"rendered":"<p>A colleague of mine was having trouble specializing a templated function. Here&#8217;s a simplified version.<\/p>\n<pre>template&lt;typename T, typename U&gt;\r\nbool same_name(T const&amp; t, U const&amp; u)\r\n{\r\n    return t.name() == u.name();\r\n}\r\n<\/pre>\n<p>They wanted to provide a specialization for the case that the parameters are a Widget and a string literal.<\/p>\n<pre>template&lt;&gt;\r\nbool same_name&lt;Widget, const char[]&gt;(Widget const&amp; widget, const char name[])\r\n{\r\n    return strcmp(widget.descriptor().name().c_str(), name) == 0;\r\n}\r\n<\/pre>\n<p>However, this failed to compile:<\/p>\n<pre style=\"white-space: pre-wrap;\">\/\/ msvc\r\nerror C2912: explicit specialization 'bool same_name&lt;Widget,const char[]&gt;(const Widget &amp;,const char [])' is not a specialization of a function template\r\n<\/pre>\n<p>What do you mean &#8220;not a specialization of a function template&#8221;? I mean doesn&#8217;t it look like a specialization of a function template? It sure follows the correct syntax for a function template specialization.<\/p>\n<p>The error message from gcc is a little more helpful:<\/p>\n<pre style=\"white-space: pre-wrap;\">error: template-id 'same_name&lt;Widget, const char []&gt;' for 'bool same_name(const Widget&amp;, const char*)' does not match any template declaration\r\n<\/pre>\n<p>Okay, so gcc recognized that it&#8217;s a specialization of a function template, but it couldn&#8217;t find a match. What is this &#8220;match&#8221; talking about?<\/p>\n<p>The error message from clang helps even more:<\/p>\n<pre style=\"white-space: pre-wrap;\">error: no function template matches function template specialization 'same_name'\r\n| bool same_name&lt;Widget, const char[]&gt;(Widget const&amp; widget, const char name[])\r\n\r\nnote: candidate template ignored: could not match 'bool (const Widget &amp;, const const char (&amp;)[])' against 'bool (const Widget &amp;, const char *)'\r\n<\/pre>\n<p>Okay, now we&#8217;re getting somewhere. The compiler is taking the specialization we provided and is unable to match it against the non-specialized version.<\/p>\n<p>And that&#8217;s where we see the problem. If we substitute <code>Widget<\/code> and <code>const char[]<\/code> into the original declaration of <code>bool same_name(T const&amp; t, U const&amp; u)<\/code>, we get<\/p>\n<pre>    bool same_name(Widget const&amp; t, const char(&amp; u)[]);\r\n<\/pre>\n<p>But this isn&#8217;t the function signature of our proposed specialization. Our proposed specialization takes a <code>const char*<\/code> as the final parameter, since function and array parameters in parameter lists are rewritten as pointers: <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/dcl.fct#4\"><b>[dcl.fct]<\/b>(4)<\/a>: &#8220;any parameter of type &#8216;array of <tt>T<\/tt>&#8216; or of function type T is adjusted to be &#8216;pointer to <tt>T<\/tt>&#8216;.&#8221;<\/p>\n<p>That&#8217;s what msvc was trying to tell us when it said &#8220;is not a specialization of a function template&#8221;: &#8220;What you wrote sure looks like a specialization of a function template, but it&#8217;s not because the signature is wrong.&#8221; Perhaps a better message would be &#8220;is not a <i>valid<\/i> specialization of a function template&#8221; or &#8220;does not <i>correspond to<\/i> a specialization of a function template.&#8221;<\/p>\n<p>A valid specialization would be<\/p>\n<pre>template&lt;&gt;\r\nbool same_name&lt;Widget, const char*&gt;(Widget const&amp; widget, const char *const&amp; name)\r\n{\r\n    return strcmp(widget.descriptor().name().c_str(), name) == 0;\r\n}\r\n<\/pre>\n<p>That sure looks clunky, but it doesn&#8217;t have to be.<\/p>\n<p>You don&#8217;t need to do specialization at all: You can use overloading.<\/p>\n<pre>bool same_name(Widget const&amp; widget, const char* name)\r\n{\r\n    return strcmp(widget.descriptor().name().c_str(), name) == 0;\r\n}\r\n<\/pre>\n<p>The nice thing about overloading is that you don&#8217;t have to be a perfect match for the original template. Here, we take the second parameter by value instead of by reference. You can even change the return value in an overload.<\/p>\n<pre>std::optional&lt;bool&gt; same_name(Widget const&amp; widget, const char* name)\r\n{\r\n    if (!widget.is_name_known()) return std::nullopt;\r\n    return strcmp(widget.descriptor().name().c_str(), name) == 0;\r\n}\r\n<\/pre>\n<p>In this case, we change the return type from <code>bool<\/code> to <code>std::optional&lt;bool&gt;<\/code> to be able to express the &#8220;I don&#8217;t know&#8221; case.<\/p>\n<p>Function templates cannot be partially specialized, but that&#8217;s okay: You can get the same effect via overloading.<\/p>\n<pre>template&lt;typename U&gt;\r\nstd::optional&lt;bool&gt; same_name(Widget const&amp; widget, U const&amp; u)\r\n{\r\n    if (!widget.is_name_known()) return std::nullopt;\r\n    return widget.name() == u.name();\r\n}\r\n<\/pre>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Class<\/th>\n<th>Function<\/th>\n<\/tr>\n<tr>\n<th>Can template<\/th>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<th>Can specialize<\/th>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<th>Can partially specialize<\/th>\n<td>Yes<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<th>Can overload<\/th>\n<td>No<\/td>\n<td>Yes<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Template functions: Don&#8217;t specialize them. Overload them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You can change more things in an overload.<\/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-111063","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You can change more things in an overload.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111063","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=111063"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111063\/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=111063"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111063"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111063"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}