Header intrusive_shared_ptr.h

The core smart pointer template and its std::atomic specialization.

template<class T, class Traits>
class intrusive_shared_ptr

A smart pointer that stores T * and delegates reference-count manipulation to Traits.

Note

The template is declared with [[clang::trivial_abi]] attribute when compiled under Clang. See trivial_abi for the rationale.

Traits requirements. Traits must expose two static methods with the following signatures:

<unspecified> add_ref(T *) noexcept
<unspecified> sub_ref(T *) noexcept

The return value of either method is ignored. The argument is never nullptr.

Member types

using pointer = T*

The stored pointer type.

using element_type = T

The pointee type.

using traits_type = Traits

The traits in use.

Construction, assignment, destruction

intrusive_shared_ptr() noexcept
intrusive_shared_ptr(std::nullptr_t) noexcept

Construct a null pointer.

intrusive_shared_ptr(const intrusive_shared_ptr &src) noexcept
intrusive_shared_ptr(intrusive_shared_ptr &&src) noexcept

Copy and move construction from the same type and traits. The move constructor performs no changes to the reference count.

template<class Y, class YTraits>
intrusive_shared_ptr(const intrusive_shared_ptr<Y, YTraits> &src) noexcept
template<class Y, class YTraits>
intrusive_shared_ptr(intrusive_shared_ptr<Y, YTraits> &&src) noexcept

Converting copy and move construction from any object of a type Y such that Y * is convertible to T *, using the same or different traits. For the move, if the source and destination traits are the same, no changes to the source reference count are performed.

intrusive_shared_ptr &operator=(const intrusive_shared_ptr &src) noexcept
intrusive_shared_ptr &operator=(intrusive_shared_ptr &&src) noexcept
template<class Y, class YTraits>
intrusive_shared_ptr &operator=(const intrusive_shared_ptr<Y, YTraits> &src) noexcept
template<class Y, class YTraits>
intrusive_shared_ptr &operator=(intrusive_shared_ptr<Y, YTraits> &&src) noexcept

Copy and move assignment, with the same type/traits and converting overloads as the constructors above. As with the move constructor, a move between identical traits performs no changes to the source reference count.

~intrusive_shared_ptr() noexcept

Non-virtual destructor.

static intrusive_shared_ptr noref(T *p) noexcept

Create a smart pointer from a raw pointer without modifying the reference count (attach).

static intrusive_shared_ptr ref(T *p) noexcept

Create a smart pointer from a raw pointer and increment the reference count (retain).

Observers

T *get() const noexcept

Return the stored pointer. The reference count is not modified.

T *operator->() const noexcept

Return the stored pointer. The reference count is not modified.

T &operator*() const noexcept

Return a reference to the pointee. Undefined if the stored pointer is nullptr.

template<class M>
M &operator->*(M T::* memptr) const noexcept

Return the result of get()->*memptr, allowing access through a pointer to member of the pointee.

explicit operator bool() const noexcept

true if the pointer is non-null.

Modifiers

T *release() noexcept

Release ownership of the stored pointer. The object is set to null and the caller assumes ownership; the reference count is not adjusted.

void reset() noexcept

Clear the stored pointer, decrementing the reference count.

void swap(intrusive_shared_ptr &other) noexcept

Swap with another pointer of the same type. See also the non-member swap().

output_param get_output_param() noexcept
inout_param get_inout_param() noexcept

Return a temporary exposing operator T**() && noexcept that yields a T** aliasing the smart pointer’s internal storage, for passing to C functions that write back a reference-counted pointer.

  • get_output_param first resets the pointer to null. Use it for output parameters, where the callee returns a freshly counted pointer.

  • get_inout_param passes the current value through unchanged. Use it for in/out parameters, where the callee both reads and replaces it.

Tip

In C++23 and later, prefer std::out_ptr / std::inout_ptr (enabled by the specializations below) over these methods.

Non-member functions

All of these are noexcept.

void swap(intrusive_shared_ptr<T, Traits> &lhs, intrusive_shared_ptr<T, Traits> &rhs) noexcept

Swap two pointers of the same type. Found by ADL only.

bool operator==(...) noexcept
bool operator!=(...) noexcept

Equality and inequality between intrusive_shared_ptr of compatible types (with any traits), raw pointers, and nullptr.

bool operator<(...) noexcept
bool operator<=(...) noexcept
bool operator>=(...) noexcept
bool operator>(...) noexcept

Ordering between intrusive_shared_ptr of compatible types (with any traits) and raw pointers. On C++20 these are replaced by a single operator<=>.

size_t hash_value(const intrusive_shared_ptr<T, Traits> &ptr) noexcept

Hash of the stored pointer value (e.g. for Boost.Hash).

template<class Char>
std::basic_ostream<Char> &operator<<(std::basic_ostream<Char> &str, const intrusive_shared_ptr<T, Traits> &ptr)

Write the stored pointer value to a stream. Found by ADL only.

template<class Dest>
Dest intrusive_const_cast(intrusive_shared_ptr src) noexcept
template<class Dest>
Dest intrusive_static_cast(intrusive_shared_ptr src) noexcept
template<class Dest>
Dest intrusive_dynamic_cast(intrusive_shared_ptr src) noexcept

Perform the equivalent of const_cast / static_cast / dynamic_cast on the argument. Dest must be a valid intrusive_shared_ptr type whose underlying type is convertible from the source’s via the corresponding cast. intrusive_dynamic_cast returns a null result if the underlying dynamic_cast fails.

Specializations

template<class T, class Traits>
struct std::out_ptr_t<intrusive_shared_ptr<T, Traits>, T*>
template<class T, class Traits>
struct std::inout_ptr_t<intrusive_shared_ptr<T, Traits>, T*>

Enable std::out_ptr / std::inout_ptr with intrusive_shared_ptr. Provided when the standard library offers <out_ptr> support.

template<class T, class Traits, class CharT>
struct std::formatter<intrusive_shared_ptr<T, Traits>, CharT>

Enable std::format with intrusive_shared_ptr. Provided when the standard library offers std::format support.

template<class T, class Traits>
struct std::hash<intrusive_shared_ptr<T, Traits>>

Enable use as a key in unordered associative containers.

template<class Traits, class T>
class std::atomic<intrusive_shared_ptr<T, Traits>>

Provides the standard std::atomic interface for intrusive_shared_ptr. It is not lock-free: operations are serialized with an internal lock.

using value_type = isptr::intrusive_shared_ptr<T, Traits>

The pointer type being wrapped.

static constexpr bool is_always_lock_free

Always false.

Construction / assignment

constexpr atomic() noexcept

Initialize with a null pointer.

atomic(value_type desired) noexcept

Initialize holding desired.

atomic(const atomic&) = delete
atomic &operator=(const atomic&) = delete

Not copyable.

~atomic() noexcept

Releases the held pointer.

value_type operator=(value_type desired) noexcept

Same as store().

operator value_type() const noexcept

Same as load().

Atomic operations

Each behaves like the matching std::atomic member.

value_type load(std::memory_order order = std::memory_order_seq_cst) const noexcept

Read the stored pointer.

void store(value_type desired, std::memory_order order = std::memory_order_seq_cst) noexcept

Replace the stored pointer.

value_type exchange(value_type desired, std::memory_order order = std::memory_order_seq_cst) noexcept

Replace the stored pointer and return the previous value.

bool compare_exchange_strong(value_type &expected, value_type desired, std::memory_order success, std::memory_order failure) noexcept
bool compare_exchange_strong(value_type &expected, value_type desired, std::memory_order order = std::memory_order_seq_cst) noexcept

Compare and swap.

bool compare_exchange_weak(value_type &expected, value_type desired, std::memory_order success, std::memory_order failure) noexcept
bool compare_exchange_weak(value_type &expected, value_type desired, std::memory_order order = std::memory_order_seq_cst) noexcept

Compare and swap. May fail spuriously.

bool is_lock_free() const noexcept

Always false.