{"id":103556,"date":"2020-03-12T07:00:00","date_gmt":"2020-03-12T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103556"},"modified":"2020-03-11T17:36:33","modified_gmt":"2020-03-12T00:36:33","slug":"20200312-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200312-00\/?p=103556","title":{"rendered":"Of what use is a type-dependent expression that is always false?"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200311-00\/?p=103553\"> Last time<\/a>, we saw how to create a type-dependent expression that is always false, and used it in a potentially-discarded statement so that the assertion failed only if the statement ended up being used.<\/p>\n<p>Another case where you want to defer a static assertion failure to instantiation is if you want to reject a particular specialization.<\/p>\n<p>Say you have a method that you want to overload, but a particular version of the overload is disallowed. You could use <code>std::<code><\/code>enable_<code><\/code>if<\/code> to remove that overload from consideration, leading to a compiler error of the form &#8220;No suitable overload found.&#8221;<\/p>\n<p>For example, suppose we have a <code>buffer_<\/code><code>view<\/code> that represents the raw bytes stored in a vector.<\/p>\n<pre>struct buffer_view\r\n{\r\n  template&lt;typename C&gt;\r\n  buffer_view(std::vector&lt;C&gt; const&amp; v) :\r\n    data(v.data()), size(v.size() * sizeof(C)) { }\r\n\r\n  \/\/ Imagine other constructors for std::array, etc.\r\n\r\n  void const* data;\r\n  std::size_t size;\r\n};\r\n<\/pre>\n<p>The idea here is that this is a buffer for passing raw bytes to another function. Therefore, in practice, you probably would add a<\/p>\n<pre>typename = std::enable_if_t&lt;std::is_trivial&lt;C&gt;::value&gt;\r\n<\/pre>\n<p>to the template parameters, so that people won&#8217;t try to pass things like <code>std::string<\/code> as a buffer. In practice, you probably also would want a second template parameter <code>std::vector&lt;T, Alloc&gt;<\/code> in order to support non-default allocators. But I&#8217;ve left off these adjustments to simplify the exposition.<\/p>\n<p>This class works great until somebody tries this:<\/p>\n<pre>std::vector&lt;bool&gt; flags;\r\nauto view = buffer_view(flags);\r\n<\/pre>\n<p>The C++ language defines a specialization <code>std::vector&lt;bool&gt;<\/code> which represents a packed bit array, rather than defining a separate type like <code>std::bitvector<\/code>. This has made a lot of people very angry and <a href=\"https:\/\/isocpp.org\/blog\/2012\/11\/on-vectorbool\"> has been widely regarded<\/a> as a <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2005\/n1847.pdf\"> bad move<\/a>.<\/p>\n<p>One of the quirks of <code>std::vector&lt;bool&gt;<\/code> is that it lacks a <code>data()<\/code> method.<\/p>\n<p>If you pass in a <code>std::vector&lt;bool&gt;<\/code>, you get the weird error from the Microsoft compiler:<\/p>\n<pre style=\"white-space: pre-wrap;\">error C2039: 'data': is not a member of 'std::vector&lt;<wbr \/>bool, std::<wbr \/>allocator&lt;_Ty&gt;&gt;'\r\n        with\r\n        [\r\n            _Ty=bool\r\n        ]\r\nnote: see declaration of 'std::vector&lt;bool, std::<wbr \/>allocator&lt;_Ty&gt;&gt;'\r\n        with\r\n        [\r\n            _Ty=bool\r\n        ]\r\nnote: see reference to function template instantiation\r\n'buffer_view::<wbr \/>buffer_view&lt;<wbr \/>bool&gt;(<wbr \/>const std::vector&lt;<wbr \/>bool, std::<wbr \/>allocator&lt;_Ty&gt;&gt; &amp;)' being compiled\r\n        with\r\n        [\r\n            _Ty=bool\r\n        ]\r\n<\/pre>\n<p>gcc and clang produce a completely bizarre error:<\/p>\n<pre style=\"white-space: pre-wrap;\">error: 'this' argument to member function 'data' has type 'const std::vector&lt;bool&gt;', but function is not marked const\r\n    data(v.data()), size(v.size() * sizeof(C)) { }\r\n         ^\r\n<\/pre>\n<p>I mean, technically, all the error messages are &#8220;correct&#8221; in the sense that all the standard requires is the generation of a diagnostic, but does not require that the diagnostic be useful.<\/p>\n<p>We might try to improve the error message by specializing the constructor for <code>std::vector&lt;bool&gt;<\/code> and deleting it.<\/p>\n<pre style=\"white-space: pre-wrap;\">template&lt;&gt;\r\nbuffer_view::buffer_view(<wbr \/>std::<wbr \/>vector&lt;<wbr \/>bool&gt; const&amp; v) = delete;\r\n<\/pre>\n<p>Now the error messages are a little better:<\/p>\n<pre style=\"white-space: pre-wrap;\">error C2280: 'buffer_view::<wbr \/>buffer_view&lt;bool&gt;(<wbr \/>const std::<wbr \/>vector&lt;bool, std::allocator&lt;_Ty&gt;&gt; &amp;)':\r\nattempting to reference a deleted function\r\n        with\r\n        [\r\n            _Ty=bool\r\n        ]\r\nnote: see declaration of 'buffer_view::buffer_view'\r\nnote: 'buffer_view::<wbr \/>buffer_view&lt;bool&gt;(<wbr \/>const std::<wbr \/>vector&lt;bool, std::allocator&lt;_Ty&gt;&gt; &amp;)': function was explicitly deleted\r\n        with\r\n        [\r\n            _Ty=bool\r\n        ]\r\n\r\ncall to deleted constructor of 'buffer_view'\r\n\r\nuse of deleted function 'buffer_view::<wbr \/>buffer_view(<wbr \/>const std::<wbr \/>vector&lt;C&gt;&amp;) [with C = bool]'\r\n<\/pre>\n<p>But it would be great if we could generate a custom error message. You might think you could do it by putting a <code>static_assert<\/code> in the body:<\/p>\n<pre>  template&lt;typename C&gt;\r\n  buffer_view(std::vector&lt;C&gt; const&amp; v) :\r\n    data(v.data()), size(v.size() * sizeof(C))\r\n  {\r\n    <span style=\"color: blue;\">static_assert(!is_same_v&lt;C, bool&gt;,\r\n      \"Can't use std::vector&lt;bool&gt;. Try std::array instead.\");<\/span>\r\n  }\r\n<\/pre>\n<p>Unfortunately, this <code>static_<\/code><code>assert<\/code> happens after the attempt to use <code>v.data()<\/code>, so the first error the developer sees is the incomprehensible one. We want our message to be the first error message, so we can quickly steer the developer in the right direction.<\/p>\n<p>So we try again with a specialization that doesn&#8217;t try to use the <code>v.data()<\/code> method, thereby avoiding the incomprehensible error message. We can then put our custom error message in the specialization.<\/p>\n<pre>template&lt;&gt;\r\nbuffer_view::buffer_view(std::vector&lt;bool&gt; const&amp; v)\r\n{\r\n  static_assert(false, \"blah blah blah\");\r\n}\r\n<\/pre>\n<p>However, this generates an error even if nobody tries to use the <code>std::vector&lt;bool&gt;<\/code> overload because the controlling expression of the <code>static_<\/code><code>assert<\/code> is not dependent upon the template type.<\/p>\n<p>So let&#8217;s make it dependent upon the template type.<\/p>\n<pre>  template&lt;typename C,\r\n           <span style=\"color: blue;\">std::enable_if_t&lt;!std::is_same_v&lt;C, bool&gt;, int&gt; = 0<\/span>&gt;\r\n  buffer_view(std::vector&lt;C&gt; const&amp; v) :\r\n    data(v.data()), size(v.size() * sizeof(C)) { }\r\n\r\n  template&lt;typename C,\r\n           <span style=\"color: blue;\">std::enable_if_t&lt;std::is_same_v&lt;C, bool&gt;, int&gt; = 0<\/span>&gt;\r\n  buffer_view(std::vector&lt;C&gt; const&amp; v)\r\n  {\r\n    <span style=\"color: blue;\">static_assert(!sizeof(C)<\/span>, \"blah blah blah\");\r\n  }\r\n<\/pre>\n<p>We create two templated constructors and let <code>enable_if<\/code> decide which one is active. For anything that isn&#8217;t <code>bool<\/code>, we activate the first one, and we activate the second one only for <code>bool<\/code>.<\/p>\n<p>The trick is that we now have a template type name &#8220;<code>C<\/code>&#8221; that we can use to generate a type-dependent always-false expression to put into the <code>static_<\/code><code>assert<\/code>. In this case, we can save a character and elide the <code>*<\/code> because we know that the type is exactly <code>bool<\/code>. We don&#8217;t need to worry about the case where <code>C<\/code> is an incomplete type or <code>void<\/code>.<\/p>\n<p>After I wrote this up, I discovered that <a href=\"https:\/\/kennykerr.ca\/\"> Kenny Kerr<\/a> came up with a simpler solution, which we&#8217;ll look at next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Deferring an assertion to instantiation.<\/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-103556","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Deferring an assertion to instantiation.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103556","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=103556"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103556\/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=103556"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103556"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103556"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}