{"id":103183,"date":"2019-12-05T07:00:00","date_gmt":"2019-12-05T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103183"},"modified":"2019-12-06T14:16:26","modified_gmt":"2019-12-06T22:16:26","slug":"20191205-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191205-00\/?p=103183","title":{"rendered":"In C++\/CX, hat pointers are contextually convertible to <CODE>bool<\/CODE>, but you can&#8217;t always <CODE>static_cast<\/CODE> them to <CODE>bool<\/CODE>"},"content":{"rendered":"<p>C++\/CX is a language extension intended to make consuming the Windows Runtime easier. It is, however, no longer the C++ projection of choice. That honor now belongs to C++\/WinRT, which allows you to consume the Windows Runtime using standard-conforming C++, no language extensions required.<\/p>\n<p>For those of you stuck with C++\/CX, here&#8217;s a little puzzle: What do these functions do?<\/p>\n<pre>bool Mystery1(Object^ o)\r\n{\r\n    if (o) {\r\n        return true;\r\n    } else {\r\n        return false;\r\n    }\r\n}\r\n\r\nbool Mystery2(Object^ o)\r\n{\r\n    return static_cast&lt;bool&gt;(o);\r\n}\r\n\r\nbool Mystery3(Object^ o)\r\n{\r\n    return bool(o);\r\n}\r\n\r\nbool Mystery4(Object^ o)\r\n{\r\n    return (bool)o;\r\n}\r\n<\/pre>\n<p>You&#8217;d think these would all be equivalent, but they&#8217;re not.<\/p>\n<p>In the first mystery function, the hat pointer <code>o<\/code> is contextually converted to <code>bool<\/code>, and that&#8217;s done by treating <code>nullptr<\/code> as falsy and anything else as truthy. In this respect, hat pointers are like star pointers.<\/p>\n<p>The remaining mystery functions take the object that <code>o<\/code> points to and attempt to unbox it to a <code>bool<\/code>, and they all behave the same way:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>If <code>o<\/code> is<\/th>\n<th>Then you get<\/th>\n<\/tr>\n<tr>\n<td><tt>(Object^)true<\/tt><\/td>\n<td><tt>true<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>(Object^)false<\/tt><\/td>\n<td><tt>false<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>nullptr<\/tt><\/td>\n<td><tt>NullReferenceException<\/tt> thrown<\/td>\n<\/tr>\n<tr>\n<td>anything else<\/td>\n<td><tt>InvalidCastException<\/tt> thrown<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If you just want to know what happens and don&#8217;t care to understand the deep metaphysical significance of those last two rows, I don&#8217;t blame you.<\/p>\n<p>But that&#8217;s probably not why you&#8217;re here. You want to understand the weird crazy world that led to the strange table above.<\/p>\n<p>What&#8217;s going on is that a <code>Object^<\/code> is really an <code>IInspectable*<\/code> under the hood. And cast operations on <code>IInspectable*<\/code> are performed by doing a <code>Query\u00adInterface<\/code>. In this case, we are casting to <code>IBox&lt;bool&gt;*<\/code>.<\/p>\n<p>If you have a <code>nullptr<\/code>, then the attempt to call <code>Query\u00adInterface<\/code> results in a null pointer dereference, hence the <code>Null\u00adReference\u00adException<\/code>.<\/p>\n<p>If the object is not a boxed <code>bool<\/code>, then the <code>Query\u00adInterface<\/code> fails with <code>E_NO\u00adINTERFACE<\/code>, which is expressed in C++\/CX as an <code>Invalid\u00adCast\u00adException<\/code>.<\/p>\n<p>For me, the weird part is that there are two different categories of results: The contextual conversion is different from the other conversions.<\/p>\n<p>It means that you get weird puzzles like this:<\/p>\n<pre>Object^ p = false;\r\nObject^ q = false;\r\n\r\nif (p)                    std::cout &lt;&lt; 1;\r\nif ((bool)p)              std::cout &lt;&lt; 2;\r\nif (static_cast&lt;bool&gt;(p)) std::cout &lt;&lt; 3;\r\nif (p == q)               std::cout &lt;&lt; 4;\r\nif (p == false)           std::cout &lt;&lt; 5;\r\nif (!p)                   std::cout &lt;&lt; 6;\r\nif ((bool)p == (bool)q)   std::cout &lt;&lt; 7;\r\n<\/pre>\n<p>What does this fragment print?<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Condition<\/th>\n<th>What&#8217;s happening<\/th>\n<th>Result<\/th>\n<\/tr>\n<tr>\n<td><tt>if (p)<\/tt><\/td>\n<td>Tests <tt>p<\/tt> against <tt>nullptr<\/tt>.<\/td>\n<td>prints <tt>1<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>if ((bool)p)<\/tt><\/td>\n<td>Unboxes <tt>p<\/tt> to <tt>bool<\/tt>.<\/td>\n<td>does not print<\/td>\n<\/tr>\n<tr>\n<td><tt>if (static_cast&lt;bool&gt;(p))<\/tt><\/td>\n<td>Unboxes <tt>p<\/tt> to <tt>bool<\/tt>.<\/td>\n<td>does not print<\/td>\n<\/tr>\n<tr>\n<td><tt>if (p == q)<\/tt><\/td>\n<td>Compares two objects for identity.<\/td>\n<td>does not print<\/td>\n<\/tr>\n<tr>\n<td><tt>if (p == false)<\/tt><\/td>\n<td>Boxes <code>false<\/code> then compares two objects for identity.<\/td>\n<td>does not print<\/td>\n<\/tr>\n<tr>\n<td><tt>if (!p)<\/tt><\/td>\n<td>Tests <tt>p<\/tt> against <tt>nullptr<\/tt>.<\/td>\n<td>does not print<\/td>\n<\/tr>\n<tr>\n<td><tt>if ((bool)p == (bool)q)<\/tt><\/td>\n<td>Unboxes <tt>p<\/tt> and <tt>q<\/tt> and compares them.<\/td>\n<td>prints 7<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Converting hat pointers to <code>bool<\/code> is very strange. Be glad you don&#8217;t have to deal with it.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191206-00\/?p=103191\"> Next time<\/a>, we&#8217;ll look at C++\/WinRT. It&#8217;ll be a lot less strange.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Because C++\/CX is weird like that.<\/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-103183","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Because C++\/CX is weird like that.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103183","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=103183"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103183\/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=103183"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103183"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}