{"id":103071,"date":"2019-11-07T07:00:00","date_gmt":"2019-11-07T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103071"},"modified":"2019-11-08T07:16:03","modified_gmt":"2019-11-08T15:16:03","slug":"20191107-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191107-00\/?p=103071","title":{"rendered":"How can I give a C++ lambda expression more than one <CODE>operator()<\/CODE>?"},"content":{"rendered":"<p>Suppose you have stored a C++ lambda expression into a variable, and you want to call it in different ways. This seems impossible, because when you define the lambda expression, you can provide only one <code>operator()<\/code>:<\/p>\n<pre>auto lambda = [captures](int v) { return v + 2; };\r\n<\/pre>\n<p>This lambda has only one way of calling it: You pass an integer and it returns an integer.<\/p>\n<p>But it turns out that you can create a lambda that can be called in multiple ways: Use an <code>auto<\/code> parameter!<\/p>\n<pre>auto lambda = [](auto p)\r\n{\r\n  if constexpr (std::is_same_v&lt;decltype(p), int&gt;) {\r\n   return p + 1;\r\n  } else {\r\n   return \"oops\";\r\n  }\r\n};\r\n\r\nauto result1 = lambda(123); \/\/ result1 is 124\r\nauto result2 = lambda('x'); \/\/ result2 is \"oops\"\r\n<\/pre>\n<p>By declaring the parameter as <code>auto<\/code>, the lambda accepts any single parameter. We then use <code>if constexpr<\/code> and <code>std::is_same_v<\/code> to see what type was actually passed, and implement the desired function body for each type.<\/p>\n<p>Notice that the different branches of the <code>if<\/code> don&#8217;t need to agree on the return type. In our example, passing an integer adds one and produces another integer. But passing anything else returns the string <code>\"oops\"<\/code>!<\/p>\n<p>You can create a bunch of tag types to make it look almost as if your lambda had member functions.<\/p>\n<pre>struct add_tax_t {};\r\nconstexpr add_tax_t add_tax;\r\n\r\nstruct apply_discount_t {};\r\nconstexpr apply_discount_t apply_discount;\r\n\r\nauto lambda = [total](auto op, auto value) mutable\r\n{\r\n  using Op = decltype(op);\r\n  if constexpr (std::is_same_v&lt;Op, add_tax_t&gt;) {\r\n   total += total * value; \/\/ value is the tax rate\r\n   return total;\r\n  } else if constexpr (std::is_same_v&lt;Op, apply_discount_t&gt;) {\r\n   total -= std::max(value, total); \/\/ value is the discount\r\n   return total;\r\n  } else {\r\n   static_assert(!sizeof(Op*), \"Don't know what you are asking me to do.\");\r\n  }\r\n};\r\n\r\nlambda(apply_discount, 5.00); \/\/ apply $5 discount\r\nlambda(add_tax, 0.10); \/\/ add 10% tax\r\n<\/pre>\n<p>So far, all of our &#8220;methods&#8221; have the same number of parameters, but you can use a parameter pack to permit different numbers of parameters:<\/p>\n<pre>auto lambda = [total](auto op, auto... args) mutable\r\n{\r\n  using Op = decltype(op);\r\n  using ArgsT = std::tuple&lt;decltype(args)...&gt;;\r\n  if constexpr (std::is_same_v&lt;Op, add_tax_t&gt;) {\r\n   auto [tax_rate] = ArgsT(args...);\r\n   total += total * tax_rate;\r\n   return total;\r\n  } else if constexpr (std::is_same_v&lt;Op, apply_discount_t&gt;) {\r\n   auto [amount, expiration] = ArgsT(args...);\r\n   if (expiration &lt; now()) {\r\n     total -= std::max(amount, total);\r\n   }\r\n   return total;\r\n  } else {\r\n   static_assert(!sizeof(Op*), \"Don't know what you are asking me to do.\");\r\n  }\r\n};\r\n<\/pre>\n<p>In this case, the <code>add_<code><\/code>tax<\/code> &#8220;method&#8221; takes a single parameter, whereas the <code>apply_<code><\/code>discount<\/code> &#8220;method&#8221; takes two.<\/p>\n<p>You could even dispatch based solely on the types and arity.<\/p>\n<pre>auto lambda = [total](auto... args) mutable\r\n{\r\n  using ArgsT = std::tuple&lt;decltype(args)...&gt;;\r\n  if constexpr (std::is_same_v&lt;ArgsT, std::tuple&lt;int, int&gt;&gt;) {\r\n   \/\/ two integers = add to total\r\n   auto [a, b] = ArgsT(args...);\r\n   total += a + b;\r\n  } else if constexpr (std::is_same_v&lt;ArgsT, std::tuple&lt;&gt;&gt;) {\r\n   \/\/ no parameters = print\r\n   print(total);\r\n  } else {\r\n   static_assert(!sizeof(Op*), \"Don't know what you are asking me to do.\");\r\n  }\r\n};\r\n<\/pre>\n<p>This might come in handy if you have a lambda that is used to accumulate something: You can pass the lambda to the function that expects to do the accumulating, and then call the lambda using a secret knock to extract the answer.<\/p>\n<pre>auto lambda = [limit, total = 0](auto value) mutable\r\n{\r\n using T = decltype(value);\r\n if constexpr (std::is_same_v&lt;T, const char*&gt;) {\r\n  \/\/ secret knock: Return total if invoked with const char* \r\n  return total;\r\n } else {\r\n  \/\/ Otherwise, just add them up until we hit the limit.\r\n  total += value;\r\n  return total &lt;= limit;\r\n }\r\n};\r\n\r\nauto unused = std::find_if_not(begin, end, std::ref(lambda));\r\nif (unused != end) print(\"Limit exceeded.\");\r\nauto total = lambda(\"total\"); \/\/ extract the total\r\n<\/pre>\n<p>This is basically a complete and utter abuse of the language, and I hope you&#8217;re ashamed of yourself.<\/p>\n<p><b>Bonus chatter<\/b>: All of this is just a way of defining a <code>struct<\/code> without having to say the word <code>struct<\/code>.<\/p>\n<pre>struct\r\n{\r\n double limit;\r\n double total = 0.00;\r\n\r\n auto add_tax(auto tax_rate) { total += total * tax_rate; }\r\n auto apply_discount(auto amount) { total -= std::max(amount, total); }\r\n auto get_total() const { return total; }\r\n} lambda{1000.00 \/* limit *\/};\r\n<\/pre>\n<p><b>Bonus bonus chatter<\/b>: Java anonymous classes provide a more straightforward syntax:<\/p>\n<pre>var lambda = new Object() {\r\n  int total = 0;\r\n  public void add(int value) { total += value; }\r\n  public int get_total() { return total; }\r\n};\r\n\r\nlambda.add(2);\r\nlambda.add(3);\r\nvar result = lambda.get_total();\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Wacky template games.<\/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-103071","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Wacky template games.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103071","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=103071"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103071\/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=103071"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103071"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103071"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}