{"id":104329,"date":"2020-10-02T07:00:00","date_gmt":"2020-10-02T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104329"},"modified":"2020-10-02T06:44:13","modified_gmt":"2020-10-02T13:44:13","slug":"20201002-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201002-00\/?p=104329","title":{"rendered":"Little gotcha with C++\/WinRT iterators: The case of the mutated temporary"},"content":{"rendered":"<p>C++\/WinRT iterators resemble iterators in many ways, most notable they resemble them enough to let you use them in ranged for loops or in standard algorithms that use input iterators.<\/p>\n<p>But they aren&#8217;t full iterators, so watch out.<\/p>\n<p>Most notably, the iterators for indexed collections (known internally as <code>fast_iterator<\/code>) support random access, such as <code>it += 2<\/code> to step forward two items, or <code>it[-1]<\/code> to read the previous item. But they are not full-fledged random access iterators.<\/p>\n<p>They aren&#8217;t proper random access iterators because the return type of the dereferencing <code>operator*<\/code> is not a reference. It&#8217;s a value.<\/p>\n<p>That means that you can&#8217;t do this:<\/p>\n<pre>auto it = collection.First();\r\n*it = replacement_value; \/\/ replace the first element\r\n<\/pre>\n<p>If the underlying type of the collection is not a class type, then the compiler will complain:<\/p>\n<pre>IVector&lt;int&gt; collection;\r\nauto start = collection.First();\r\n*start = 42; \/\/ C2106: '=' left operand must be an l-value\r\n<\/pre>\n<p>But if the underlying type is of class type, then the class&#8217;s assignment operator will be used. You&#8217;re assigning to a temporary object, which is legal, though not particularly useful.<\/p>\n<pre>IVector&lt;Point&gt; collection;\r\nauto start = collection.First();\r\n*start = Point{ 1.0f, 2.0f }; \/\/ doesn't do what you think\r\nstart[0].X = 1.0f; \/\/ doesn't do what you think\r\n\r\nIVector&lt;Class&gt; collection;\r\nauto start = collection.First();\r\n*start = Class(); \/\/ doesn't do what you think\r\n<\/pre>\n<p>In both cases, what you&#8217;re doing is assigning a new object to (or in the case of <code>.X<\/code>, mutating) the temporary object returned by <code>operator*<\/code>, and then throwing the temporary away. The original collection remains unchanged.<\/p>\n<p>For reference types like the imaginary Windows Runtime class <code>Class<\/code>, this is largely not a problem, because the C++\/WinRT projection of Windows Runtime classes is as a reference-counted object (similar to <code>shared_ptr<\/code>), so you can invoke methods, and those methods will affect the underlying shared object.<\/p>\n<p>But for value types like <code>Point<\/code>, this is a hidden gotcha.<\/p>\n<p><b>Bonus chatter<\/b>: The dereferencing <code>operator*<\/code> could have returned a proxy object which supported conversion to <code>T<\/code> (which performs a <code>GetAt<\/code>) or assignment from <code>T<\/code> (which performs a <code>SetAt<\/code>), but that would result in a different kind of confusion, because<\/p>\n<pre>auto v = *it;\r\n<\/pre>\n<p>wouldn&#8217;t actually produce the value. It would merely produce a proxy. And that would be really weird:<\/p>\n<pre>void Something(T value);\r\n\r\nIIterable&lt;T&gt; it = ...;\r\nauto v = *it;\r\n*it = new_value;\r\nSomething(v); \/\/ passes new_value!\r\n<\/pre>\n<p>The problem is that <code>auto v = *it<\/code> deduces that <code>v<\/code> is a proxy object. It is only when the proxy is converted to a <code>T<\/code> that the <code>GetAt<\/code> is performed. In the above code fragment, that happens at the call to <code>Something<\/code>, and that means that when <code>GetAt<\/code> is called, it will fetch the <code>new_value<\/code> that was assigned in the meantime.<\/p>\n<p>To avoid surprises like this, the C++\/WinRT iterators are strictly input iterators.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You are operating on copies, not the originals.<\/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-104329","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You are operating on copies, not the originals.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104329","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=104329"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104329\/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=104329"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104329"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104329"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}