{"id":111494,"date":"2025-08-22T07:00:00","date_gmt":"2025-08-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111494"},"modified":"2025-08-15T16:14:42","modified_gmt":"2025-08-15T23:14:42","slug":"20250822-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250822-00\/?p=111494","title":{"rendered":"Thoughts on creating a tracking pointer class, part 10: Proper conversion"},"content":{"rendered":"<p>Last time, we <a title=\"Thoughts on creating a tracking pointer class, part 9: Conversion\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250821-00\/?p=111492\"> added the ability to convert tracking pointers to non-const objects into tracking pointers to const objects<\/a>, but we noted that there&#8217;s a problem.<\/p>\n<p>The problem is that our change accidentally enabled the reverse conversion: From const to non-const.<\/p>\n<p>We want to be able to convert non-const to const, but not vice versa, so let&#8217;s require the source to be non-const.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct tracking_ptr : tracking_ptr_base&lt;std::remove_cv_t&lt;T&gt;&gt;\r\n{\r\nprivate:\r\n    using base = tracking_ptr_base&lt;std::remove_cv_t&lt;T&gt;&gt;;\r\n    <span style=\"border: solid 1px currentcolor;\">using MP = tracking_ptr&lt;std::remove_cv_t&lt;T&gt;&gt;;<\/span>\r\n\r\npublic:\r\n    T* get() const { return this-&gt;tracked; }\r\n\r\n    using base::base;\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">tracking_ptr(MP const&amp; other) : base(other) {}      <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">tracking_ptr(MP&amp;&amp; other) : base(std::move(other)) {}<\/span>\r\n};\r\n<\/pre>\n<p>The conversion operators now require a tracking pointer to a non-const object (which to reduce typing we call <code>MP<\/code> for <i>mutable pointer<\/i>). The const-to-const version is inherited from the base class.<\/p>\n<p>Inheriting the constructors is particularly convenient because it avoids redefinition conflicts. If we didn&#8217;t have inherited constructors, we would have started with<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct tracking_ptr\r\n{\r\nprivate:\r\n    using MP = tracking_ptr&lt;std::remove_cv_t&lt;T&gt;&gt;;\r\n\r\npublic:\r\n    tracking_ptr(tracking_ptr const&amp; other);\r\n    tracking_ptr(MP const&amp; other);\r\n\r\n    tracking_ptr(tracking_ptr&amp;&amp; other);\r\n    tracking_ptr(MP&amp;&amp; other);\r\n};\r\n<\/pre>\n<p>But this doesn&#8217;t work with <code>tracking_ptr&lt;Widget&gt;<\/code> because you now have pairs of identical constructors since the &#8220;non-const-to-<code>T<\/code>&#8221; versions are duplicates of the copy and move constructor when <code>T<\/code> is itself non-const. Substituting <code>T<\/code> = <code>Widget<\/code>, we get<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct tracking_ptr\r\n{\r\nprivate:\r\n    using MP = tracking_ptr&lt;Widget&gt;;\r\n\r\npublic:\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">tracking_ptr(tracking_ptr&lt;Widget&gt; const&amp; other);<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">tracking_ptr(tracking_ptr&lt;Widget&gt; const&amp; other);<\/span>\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">tracking_ptr(tracking_ptr&lt;Widget&gt;&amp;&amp; other);<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">tracking_ptr(tracking_ptr&lt;Widget&gt;&amp;&amp; other);<\/span>\r\n};\r\n<\/pre>\n<p>And the compiler complains that you declared the same constructor twice. You would have to use SFINAE to remove the second one.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct tracking_ptr\r\n{\r\nprivate:\r\n    using MP = tracking_ptr&lt;std::remove_cv_t&lt;T&gt;&gt;;\r\n\r\npublic:\r\n    tracking_ptr(tracking_ptr const&amp; other);\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">template&lt;typename = std::enable_if&lt;std::is_const_v&lt;T&gt;&gt;&gt;<\/span>\r\n    tracking_ptr(MP const&amp; other);\r\n\r\n    tracking_ptr(tracking_ptr&amp;&amp; other);\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">template&lt;typename = std::enable_if&lt;std::is_const_v&lt;T&gt;&gt;&gt;<\/span>\r\n    tracking_ptr(MP&amp;&amp; other);\r\n};\r\n<\/pre>\n<p>On the other hand, redeclaring an inherited constructor overrides it, so we can just declare our constructors and not worry about conflicts.<\/p>\n<p>But wait, our attempt to fix this problem introduced a new problem. We&#8217;ll look at that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Making sure you cannot remove qualifiers.<\/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":[],"class_list":["post-111494","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing"],"acf":[],"blog_post_summary":"<p>Making sure you cannot remove qualifiers.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111494","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=111494"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111494\/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=111494"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111494"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111494"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}