Last time, we [added the ability to create non-modifying tracking pointers](https://devblogs.microsoft.com/oldnewthing/20250819-00/?p=111488’ TITLE=), but it required that you start with a non-const trackable object. But what if you want to create a non-modifying tracking pointer to an object to which you have only a const reference?

It took me a few tries before I hit upon the simple solution.¹

We just need to make the trackable object’s m_trackers mutable, and then patch up the consequences.

template<typename T> struct trackable_object { ⟦ … ⟧

tracking_ptr<T> track() noexcept {
    return { owner() };
}

tracking_ptr<const T> track() const noexcept {
    return { owner() };                       
}                                             

tracking_ptr<const T> ctrack() const noexcept {
    return { owner() };
}

private: friend struct tracking_ptr_base<T>;

T* owner() const noexcept {
    return const_cast<T*>(static_cast<const T*>(this));
}

tracking_node mutable m_trackers;

⟦ ... ⟧

};

After making the m_trackers mutable, we can make the ctrack() method const. We may as well also add a track() const that produces a read-only tracker from a const object. This is analogous to how C++ standard library container begin() and and end() methods produce read-only iterators if obtained from const containers.

Casting away const when creating and updating the tracking_ptr<const T> is okay because the tracking_ptr<const T>‘s get() method will reapply const before giving it to the client, and the only other use of the pointer is to access the m_trackers, which is now mutable.

There’s still a problem: Although you can convert a T* to a const T*, a std::unique_ptr<T> to a std::unique_ptr<const T>, and a std::shared_ptr<T> to a std::shared_ptr<const T>, you cannot convert a tracking_ptr<T> to a tracking_ptr<const T>. We’ll fix that next time.

Bonus chatter: I considered whether I should also support tracking_ptr<volatile T> and tracking_ptr<const volatile T>. It wouldn’t be hard, but it would be extra typing. I decided not to, on the grounds that the C++ standard library typically doesn’t bother with volatile either: Standard containers have begin() and cbegin() but not vbegin() or cvbegin().

¹ As Blaise Pascal is reported to have written, “If I had more time, I would have written a shorter letter.” Sometimes it takes a lot of work to come up with what ends up looking easy.

The post Thoughts on creating a tracking pointer class, part 8: Tracking const objects appeared first on The Old New Thing.


From The Old New Thing via this RSS feed