{"id":108289,"date":"2023-06-05T07:00:00","date_gmt":"2023-06-05T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108289"},"modified":"2023-06-06T11:48:56","modified_gmt":"2023-06-06T18:48:56","slug":"20230605-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230605-00\/?p=108289","title":{"rendered":"It&#8217;s great that you provide operator overloads, but it&#8217;s also nice to have names"},"content":{"rendered":"<p>Operator overloading.<\/p>\n<p>Looks great. Reduces verbosity.<\/p>\n<p>Until it doesn&#8217;t.<\/p>\n<p>Consider this overloaded function call operator:<\/p>\n<pre>struct StorageLoader\r\n{\r\n    template&lt;typename DataType&gt;\r\n    DataType operator()(StorageOptions&lt;DataType&gt; const* options);\r\n};\r\n<\/pre>\n<p>The idea is that you can use the function call operator on a <code>Storage\u00adLoader<\/code> object to load data from storage, using a <code>StorageOptions<\/code> to describe how you want it to be loaded.<\/p>\n<pre>StorageOptions&lt;Data1&gt; data1Options;\r\ndata1Options.ignore_missing(true);\r\n\r\nStorageLoader storageLoader;\r\nData1 data1 = storageLoader(&amp;data1Options);\r\n<\/pre>\n<p>The parameter is accepted as a pointer so you can pass <code>nullptr<\/code> to indicate that you accept all defaults.<\/p>\n<pre>\/\/ Oops, this doesn't work.\r\nData1 data1 = storageLoader(nullptr);\r\n<\/pre>\n<p>The <code>nullptr<\/code> doesn&#8217;t work because the compiler can&#8217;t read your mind to figure out which overload you&#8217;re trying to call. You have to help it along, either by refining the type of the parameter:<\/p>\n<pre>Data1 data1 = storageLoader(static_cast&lt;StorageOptions&lt;Data1&gt;*&gt;(nullptr));\r\n<\/pre>\n<p>or by explicitly specializing the function call operator.<\/p>\n<pre>Data1 data1 = storageLoader.operator()&lt;Data1&gt;(nullptr);\r\n<\/pre>\n<p>Neither of these is very attractive, and they certainly defeat any conciseness benefit of an overloaded operator.<\/p>\n<p>I personally am a fan of giving named function equivalents to overloaded operators, particularly if they are templated. In this case, I would have done something like<\/p>\n<pre>struct StorageLoader\r\n{\r\n    template&lt;typename DataType&gt;\r\n    DataType Load(StorageOptions&lt;DataType&gt; const* options);\r\n\r\n    template&lt;typename DataType&gt;\r\n    DataType operator()(StorageOptions&lt;DataType&gt; const* options)\r\n    { return Load(options); }\r\n};\r\n<\/pre>\n<p>The function call operator is just a convenient shorthand for calling the <code>Load<\/code> method.<\/p>\n<pre>\/\/ Using function call operator\r\ndata1 = storageLoader(&amp;data1Options);\r\n\r\n\/\/ Using named method\r\ndata1 = storageLoader.Load(&amp;data1Options);\r\n\r\n\/\/ Named method works better for nullptr\r\ndata1 = storageLoader.Load&lt;Data1&gt;(nullptr);\r\n<\/pre>\n<p>And then I can make the parameter to <code>Load<\/code> default to <code>nullptr<\/code>:<\/p>\n<pre>struct StorageLoader\r\n{\r\n    template&lt;typename DataType&gt;\r\n    DataType Load(StorageOptions&lt;DataType&gt; const* options\r\n                  <span style=\"border: solid 1px currentcolor;\">= nullptr<\/span>);\r\n\r\n    ...\r\n};\r\n<\/pre>\n<p>which allows you to write<\/p>\n<pre>\/\/ If no parameters, then use default options\r\ndata1 = storageLoader.Load&lt;Data1&gt;();\r\n<\/pre>\n<p>Sometimes, the meaning of an overloaded operator is unclear, in which case having an explicit name also helps avoid confusion over what it does. (I&#8217;m looking at you, <a title=\"On the overloading of the address-of operator &amp; in smart pointer classes\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20221010-00\/?p=107269\"> overloaded address-of operator<\/a>.)<\/p>\n<p>Also, giving a name to the overloaded operator makes generating a pointer-to-method a little less awkward.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Avoiding the need to invoke the operator explicitly as a specialized template, among other things.<\/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-108289","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Avoiding the need to invoke the operator explicitly as a specialized template, among other things.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108289","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=108289"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108289\/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=108289"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108289"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108289"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}