{"id":105061,"date":"2021-04-07T07:00:00","date_gmt":"2021-04-07T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105061"},"modified":"2021-04-06T21:49:10","modified_gmt":"2021-04-07T04:49:10","slug":"20210407-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210407-00\/?p=105061\/","title":{"rendered":"C++ coroutines: Accepting types via return_void and return_value"},"content":{"rendered":"<p>Last time, we <a title=\"C++ coroutines: Building a result holder for movable types\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210406-00\/?p=105057\"> built the result holder for our <code>simple_task<\/code> coroutine<\/a> and showed how to move values into the holder and move them back out.<\/p>\n<p>So now we&#8217;re actually going to move them in.<\/p>\n<pre>    template&lt;typename T&gt;\r\n    struct simple_promise : simple_promise_base&lt;T&gt;\r\n    {\r\n        \/\/ \u27e6implement return_value\u27e7 \u2254\r\n        void return_value(T&amp;&amp; value)\r\n        {\r\n            this-&gt;set_value(std::forward&lt;T&gt;(value));\r\n        }\r\n\r\n        template&lt;typename Dummy = void&gt;\r\n        std::enable_if_t&lt;!std::is_reference_v&lt;T&gt;, Dummy&gt;\r\n            return_value(T const&amp; value)\r\n        {\r\n            this-&gt;set_value(value);\r\n        }\r\n    };\r\n<\/pre>\n<p>In the case where <code>T<\/code> is not <code>void<\/code>, we have a few different cases to deal with: <code>T<\/code> could be an lvalue reference, an rvalue reference, or a non-reference.<\/p>\n<p>Let&#8217;s write out the possibilities. Let <code>U<\/code> the result of removing all references from <code>T<\/code>.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<th>Object<\/th>\n<th>Lvalue reference<\/th>\n<th>Rvalue reference<\/th>\n<\/tr>\n<tr>\n<td><tt>T<\/tt><\/td>\n<td><tt>U<\/tt><\/td>\n<td><tt>U&amp;<\/tt><\/td>\n<td><tt>U&amp;&amp;<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>T&amp;&amp;<\/tt><\/td>\n<td><tt>U&amp;&amp;<\/tt><\/td>\n<td><tt>U&amp;<\/tt><\/td>\n<td><tt>U&amp;&amp;<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>T const&amp;<\/tt><\/td>\n<td><tt>U const&amp;<\/tt><\/td>\n<td><tt>U&amp;<\/tt><\/td>\n<td><tt>U&amp;<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Reference collapsing rules say that adding <code>&amp;&amp;<\/code> to a reference has no effect. Furthermore, references are already immutable, so adding <code>const<\/code> also has no effect. Therefore <code>T const&amp;<\/code> is the same as <code>T&amp;<\/code> when <code>T<\/code> is a reference. The reference collapsing rules say that adding a single <code>&amp;<\/code> converts an rvalue reference to an lvalue reference and leaves lvalue references unchanged.\u00b9<\/p>\n<p>Okay, so let&#8217;s apply the above table to our situation.<\/p>\n<p>If <code>T<\/code> is not a reference, then we are in the <code>U<\/code> column, and we want both overloads. The first will move the value and the second will copy it.<\/p>\n<p>If <code>T<\/code> is an lvalue reference, then we are in the <code>U&amp;<\/code> column, so <code>T&amp;&amp;<\/code> and <code>T const&amp;<\/code> are the same thing. The two overloads conflict. We want only the first overload and we should delete the second.<\/p>\n<p>If <code>T<\/code> is an rvalue reference, then we are in the <code>U&amp;&amp;<\/code> column. We don&#8217;t want to accept lvalue references, because they will create an error deep inside <code>set_value<\/code> which will not be obvious to interpret for those unfamiliar with our implementation. Let&#8217;s just delete the second overload to convert the error message to a more understandable &#8220;cannot bind an lvalue to an rvalue reference.&#8221; (This is another example of compiler error message metaprogramming.)<\/p>\n<p>Putting this all together says that we want to delete the second overload if <code>T<\/code> is any kind of reference. So we use SFINAE to remove it.<\/p>\n<p>The case where <code>T<\/code> is <code>void<\/code> is much more straightforward:<\/p>\n<pre>    template&lt;&gt;\r\n    struct simple_promise&lt;void&gt; : simple_promise_base&lt;void&gt;\r\n    {\r\n        \/\/ \u27e6implement return_void\u27e7 \u2254\r\n        void return_void()\r\n        {\r\n            this-&gt;set_value();\r\n        }\r\n    };\r\n<\/pre>\n<p>No weird reference shenanigans. There&#8217;s only one kind of void, and we want to call <code>set_value<\/code> with nothing.<\/p>\n<p>Note that we had to put an explicit <code>this-&gt;<\/code> prefix on our calls to <code>set_value<\/code> to tell the compiler that <code>set_value<\/code> is a dependent name. Otherwise, two-phase lookup would interpret it as a non-dependent name and try to resolve it in the enclosing scope.<\/p>\n<p>Next time, we&#8217;ll look more closely at the awaiter used when the caller <code>co_await<\/code>s the <code>simple_task<\/code>.<\/p>\n<p>\u00b9 The way I remember the reference collapsing rules is to rephrase it as &#8220;lvalues are sticky.&#8221; Once there&#8217;s an lvalue reference anywhere in the reference chain, the final result is an lvalue.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A little bit of SFINAE tweaking.<\/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-105061","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A little bit of SFINAE tweaking.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105061","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=105061"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105061\/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=105061"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105061"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105061"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}