{"id":111484,"date":"2025-08-15T07:00:00","date_gmt":"2025-08-15T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111484"},"modified":"2025-08-15T08:50:37","modified_gmt":"2025-08-15T15:50:37","slug":"20250815-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250815-00\/?p=111484","title":{"rendered":"Thoughts on creating a tracking pointer class, part 5: Copying our tracking pointer"},"content":{"rendered":"<p><a title=\"Thoughts on creating a tracking pointer class, part 4\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250814-00\/?p=111482\"> Our previous attempt to create a tracking pointer class<\/a> had the perhaps-undesirable property that copying a const tracking pointer is not permitted. This requirement arose because copying a tracking pointer causes the copy to be linked into the circular linked list of the source, and that means modifying the link pointers in the source. Can we work around this?<\/p>\n<p>The secret here is realizing that it&#8217;s not important where the copy gets inserted into the circular linked list. The order of items in the list is not significant. So don&#8217;t use the source pointer as the insertion point for the copy. Instead, use the source&#8217;s tracking anchor node, which is non-const.<\/p>\n<pre>    tracking_ptr(tracking_ptr <span style=\"border: solid 1px currentcolor;\">const<\/span>&amp; other) noexcept :\r\n        <span style=\"border: solid 1px currentcolor;\">tracking_node(copy_node(other))<\/span>,\r\n        tracked(other.tracked) { }\r\n\r\n    tracking_ptr&amp; operator=(tracking_ptr <span style=\"border: solid 1px currentcolor;\">const<\/span>&amp; other) noexcept {\r\n        tracked = other.tracked;\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">if (tracked) {              <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    join(trackers(tracked));<\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">} else {                    <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    disconnect();           <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">}                           <\/span>\r\n        return *this;\r\n    }\r\n\r\nprivate:\r\n    \u27e6 ... as before ... \u27e7\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">tracking_node copy_node(tracking_ptr const&amp; other) noexcept<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                          <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    if (other.tracked) {                                   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        return tracking_node(as_join{},                    <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">                             trackers(other.tracked));     <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    } else {                                               <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        return tracking_node(as_solo{});                   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    }                                                      <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                          <\/span>\r\n\r\n    T* tracked;\r\n\r\n<\/pre>\n<p>Switching to the anchor node does have a complication that we need to check for a null pointer in the source before we try to get the target&#8217;s trackers. If the source tracks a null pointer, then that means that we are copying an expired tracking pointer, so the copy should also be empty. <!-- backref: How can I choose a different C++ constructor at runtime? --> We use the helper function trick to choose a constructor at runtime.<\/p>\n<p>Note that this solution takes advantage of const\/non-const aliasing. We are modifying a const object through a non-const path. This type of aliasing is normally a compiler&#8217;s nightmare:<\/p>\n<pre>bool test(int const&amp; v, int* p)\r\n{\r\n    auto old_value = v;\r\n    *p = 42;\r\n    return old_value == 99;\r\n}\r\n<\/pre>\n<p>The compiler cannot optimize this to<\/p>\n<pre>bool test(int const&amp; v, int* p)\r\n{\r\n    *p = 42;\r\n    return v == 99;\r\n}\r\n<\/pre>\n<p>because of the possibility that somebody called it like this:<\/p>\n<pre>int v = 99;\r\ntest(v, &amp;v);\r\n<\/pre>\n<p><b>Bonus chatter<\/b>: The Itanium processor had special instructions to <a title=\"The Itanium processor, part 8: Advanced loads\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150805-00\/?p=91171\"> allow compilers to perform this optimization in the mainline path, with a fallback if it turned out that <code>v == &amp;p<\/code><\/a> after all.<\/p>\n<pre>        \/\/ no \"alloc\" needed - lightweight leaf function\r\n        \/\/ on entry, r32 = address of v, r33 = p\r\n        ld4.a   r31 = [r32]             \/\/ fetch v into r31 in advance\r\n        mov     r30 = 42                \/\/ r30 = constant 42\r\n        mov     r29 = 99 ;;             \/\/ r29 = constant 99\r\n        st4     [r33] = r30 ;;          \/\/ write 42 to *p\r\n        <span style=\"border: solid 1px currentcolor;\">ld4.c.nc r31 = [r32]<\/span>            \/\/ reload v if necessary\r\n        cmp.eq  p6, p7 = r31, r29 ;;    \/\/ v == 99?\r\n(p6)    mov     ret0 = 1                \/\/ result is 1 if true\r\n(p7)    mov     ret0 = 0                \/\/ result is 0 if false\r\n        br.ret.sptk.many rp             \/\/ return\r\n<\/pre>\n<p>Next time, we&#8217;ll add a <code>ctrack<\/code> method for creating a tracking pointer that produces a <code>const T<\/code>. For the times you want to let somebody track an object, but not modify it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to copy from a const tracking pointer.<\/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-111484","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>How to copy from a const tracking pointer.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111484","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=111484"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111484\/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=111484"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111484"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111484"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}