{"id":102595,"date":"2019-06-17T07:00:00","date_gmt":"2019-06-17T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102595"},"modified":"2019-06-16T21:28:07","modified_gmt":"2019-06-17T04:28:07","slug":"20190617-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190617-00\/?p=102595","title":{"rendered":"A simple workaround for the fact that <CODE>std::equal<\/CODE> takes its predicate by value"},"content":{"rendered":"<p>The versions of the <code>std::equal<\/code> function that takes a binary predicate accepts the predicate by value, which means that if you are using a functor, it will be copied, which may be unnecessary or unwanted.<\/p>\n<p>In my case, the functor had a lot of state, and I didn&#8217;t want to copy it.<\/p>\n<pre>class comparer\r\n{\r\n  ...\r\n\r\n  template&lt;typename R&gt;\r\n  bool ranges_equiv(R const&amp; left, R const&amp; right)\r\n  {\r\n    using T = typename std::decay_t&lt;decltype(*begin(left))&gt;;\r\n    return std::equal(\r\n      begin(left), end(left),\r\n      begin(right), end(right),\r\n      equiv&lt;T&gt;);\r\n  }\r\n\r\n  template&lt;typename T&gt;\r\n  bool equiv(T const&amp; left, T const&amp; right) = delete;\r\n\r\n  template&lt;&gt;\r\n  bool equiv(Doodad const&amp; left, Doodad const&amp; right)\r\n  {\r\n    return (!check_names || equiv(left.Name(), right.Name())) &amp;&amp;\r\n           (!check_children || ranges_equiv(left.Children(), right.Children()));\r\n  }\r\n\r\n  ... other overloads omitted ...\r\n};\r\n<\/pre>\n<p>The idea behind the <code>comparer<\/code> is that you configure it with information about what you care about and what you don&#8217;t, and then you call <code>equiv<\/code> and let it walk the object hierarchy comparing the things you asked for according to the rules you specified.<\/p>\n<p>This works great, except that <code>std::equal<\/code> copies its predicate, and our <code>comparer<\/code> is somewhat expensive to copy, since it may have lots of configuration <code>std::string<\/code>s and stuff. What we&#8217;re looking for is a version that takes the predicate by reference, so that we can use the same <code>comparer<\/code> all the way down.<\/p>\n<p>The workaround is to replace the predicate with something that is cheap to copy.<\/p>\n<pre>  template&lt;typename R&gt;\r\n  bool ranges_equiv(R const&amp; left, R const&amp; right)\r\n  {\r\n    return std::equal(\r\n      begin(left), end(left),\r\n      begin(right), end(right),\r\n      <span style=\"color: blue;\">[this](auto&amp;&amp; l, auto&amp;&amp; r) { return equiv(l, r); }<\/span>);\r\n  }\r\n<\/pre>\n<p>Instead of passing a full <code>comparer<\/code> object, we pass a lambda that captures the <code>comparer<\/code>&#8216;s <code>this<\/code> pointer. This lambda is cheap to copy, and it allows us to reuse the same <code>comparer<\/code> all the way down the object hierarchy.<\/p>\n<p>This solution looks obvious in retrospect, but I got all hung up trying to create a cheap copyable object, like a nested type called <code>compare_forwarder<\/code> that kept a <code>std::reference_wrapper<\/code> to the <code>comparer<\/code>, before realizing that I was just writing a verbose version of a lambda.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pass something else that is easy to copy.<\/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-102595","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Pass something else that is easy to copy.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102595","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=102595"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102595\/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=102595"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102595"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102595"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}