{"id":105374,"date":"2021-06-28T07:00:00","date_gmt":"2021-06-28T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105374"},"modified":"2021-06-28T08:21:28","modified_gmt":"2021-06-28T15:21:28","slug":"20210628-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210628-00\/?p=105374","title":{"rendered":"The initializing constructor looks like an assignment, but it isn&#8217;t"},"content":{"rendered":"<p>Some time ago, I warned about <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210115-00\/?p=104719\"> the perils of the accidental C++ conversion constructor<\/a>: A single-parameter constructor is considered by default to be a conversion constructor; you can opt out of this by marking the constructor <code>explicit<\/code>.<\/p>\n<p>I gave as an example this class:<\/p>\n<pre>class Buffer\r\n{\r\npublic:\r\n  Buffer(size_t capacity);\r\n  Buffer(std::initializer_list&lt;int&gt; values);\r\n};\r\n<\/pre>\n<p>The <code>size_t<\/code> constructor is not marked as <code>explicit<\/code>, so it is a conversion constructor. And that permits weird things like this:<\/p>\n<pre>Buffer b = 1; \/\/ um...\r\n<\/pre>\n<p>What exactly is happening here?<\/p>\n<p>A common misconception is that what&#8217;s happening is that a temporary <code>Buffer<\/code> is created (with the capacity 1), and then that temporary buffer is assigned to the destination buffer <code>b<\/code>.<\/p>\n<p>That&#8217;s not what&#8217;s happening. You can prove this by deleting the assignment operators.<\/p>\n<pre>class Buffer\r\n{\r\npublic:\r\n  Buffer(size_t capacity);\r\n  Buffer(std::initializer_list&lt;int&gt; values);\r\n  <span style=\"color: blue;\">Buffer&amp; operator=(Buffer const&amp;) = delete;\r\n  Buffer&amp; operator=(Buffer&amp;&amp;) = delete;<\/span>\r\n};\r\n\r\nBuffer b = 1; \/\/ still compiles\r\n<\/pre>\n<p>(Deleting the move assignment operator is redundant because declaring the copy assignment operator automatically suppresses the implicit move assignment operator. But I deleted it explicitly for emphasis.)<\/p>\n<p>Even though there is an equal-sign in the statement, there is no actual assignment.<\/p>\n<p>There can&#8217;t be an assignment, if you think about it, because the assignment operator assumes that <code>this<\/code> refers to an already-constructed object. But we don&#8217;t have a constructed object yet.<\/p>\n<p>According to the language rules,<\/p>\n<pre>Buffer b = 1;\r\n<\/pre>\n<p>is a <i>copy-initialization<\/i>, and the copy initialization is performed by taking the thing on the right-hand side and, if the types don&#8217;t match,\u00b9 it looks for a conversion constructor.<\/p>\n<p>The equals sign doesn&#8217;t mean assignment here. It&#8217;s just a quirk of the syntax.<\/p>\n<p>\u00b9 If the types do match, then &#8220;the initializer expression is used to initialize the destination object.&#8221; At this point <i>copy elision<\/i> kicks in:<\/p>\n<pre>extern Buffer get_buffer();\r\n\r\nBuffer b = get_buffer();\r\n<\/pre>\n<p>The <code>Buffer<\/code> returned by <code>get_buffer()<\/code> is placed directly into the memory occupied by <code>b<\/code>.<\/p>\n<p>Copy elision also means that<\/p>\n<pre>Buffer b = Buffer(1);\r\n<\/pre>\n<p>does not create a temporary <code>Buffer<\/code> of capacity 1, and then construct <code>b<\/code> from that temporary buffer. Instead, the <code>Buffer<\/code> of capacity 1 is constructed directly into <code>b<\/code>. The result is the same as <code>Buffer b(1);<\/code>.<\/p>\n<p>Since the copy elision rule can be repeated,<\/p>\n<pre>Buffer b = Buffer(Buffer(Buffer(1)));\r\n<\/pre>\n<p>is also the same as <code>Buffer b(1);<\/code>, because each repetition of the rule strips away one of the calls to <code>Buffer(...)<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There is no assignment going on; it&#8217;s just a quirk of syntax.<\/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-105374","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>There is no assignment going on; it&#8217;s just a quirk of syntax.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105374","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=105374"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105374\/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=105374"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105374"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105374"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}