Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions benchmarks/proxy_management_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ struct SmallObject3;

} // namespace

namespace pro::inline v4::details {
namespace pro {

template <>
struct tr_override_traits<SmallObject3> : applicable_traits {};
struct is_bitwise_trivially_relocatable<SmallObject3> : std::true_type {};

} // namespace pro::inline v4::details
} // namespace pro

namespace {

Expand Down
1 change: 1 addition & 0 deletions docs/spec/.pages
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ nav:
- explicit_conversion_dispatch<br />conversion_dispatch: explicit_conversion_dispatch
- facade_aware_overload_t: facade_aware_overload_t.md
- implicit_conversion_dispatch: implicit_conversion_dispatch
- is_bitwise_trivially_relocatable: is_bitwise_trivially_relocatable.md
- not_implemented: not_implemented.md
- operator_dispatch: operator_dispatch
- proxy_indirect_accessor: proxy_indirect_accessor.md
Expand Down
24 changes: 3 additions & 21 deletions docs/spec/ProFacade.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,9 @@ Relocatability is defined as *move-construct an object and then destroy the orig
| Value | Requirement on `P` |
| ------------------------------ | ------------------------------------------------------------ |
| `constraint_level::none` | None |
| `constraint_level::nontrivial` | `std::is_move_constructible_v<P> && std::is_destructible_v<P>` |
| `constraint_level::nothrow` | `std::is_nothrow_move_constructible_v<P> && std::is_nothrow_destructible_v<P>` |
| `constraint_level::trivial` | *trivially relocatable* (see below) |

C++26 introduces the type trait `std::is_trivially_relocatable_v` ([P2786R13](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html)). The library evaluates *trivial relocatability* as follows:

- When `std::is_trivially_relocatable_v<P>` is available
– If the trait evaluates to `true`, the requirement is met.
– If it evaluates to `false`, the library falls back to the **allow-list** (see below).

- When the trait is **not** available
– A conservative check is used: `std::is_trivially_move_constructible_v<P> && std::is_trivially_destructible_v<P>`
– If that check is `false`, the allow-list is consulted.

The allow-list contains types that are known to be safely relocatable even when the compiler cannot confirm it:

- `std::unique_ptr<T, D>` when `D` is trivially relocatable
- `std::shared_ptr<T>`
- `std::weak_ptr<T>`

This strategy avoids false negatives caused by gaps in current compiler implementations of the C++26 feature set while maintaining safety.
| `constraint_level::nontrivial` | `(std::is_move_constructible_v<P> && std::is_destructible_v<P>) || `[`is_bitwise_trivially_relocatable_v<P>`](is_bitwise_trivially_relocatable.md) |
| `constraint_level::nothrow` | `(std::is_nothrow_move_constructible_v<P> && std::is_nothrow_destructible_v<P>) || `[`is_bitwise_trivially_relocatable_v<P>`](is_bitwise_trivially_relocatable.md) |
| `constraint_level::trivial` | [`is_bitwise_trivially_relocatable_v<P>`](is_bitwise_trivially_relocatable.md) |

## See Also

Expand Down
1 change: 1 addition & 0 deletions docs/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ This document provides the API specifications for the C++ library Proxy (version
| [`explicit_conversion_dispatch`<br />`conversion_dispatch`](explicit_conversion_dispatch/README.md) | Dispatch type for explicit conversion expressions with accessibility |
| [`facade_aware_overload_t`](facade_aware_overload_t.md) | Specifies a facade-aware overload template |
| [`implicit_conversion_dispatch`](implicit_conversion_dispatch/README.md) | Dispatch type for implicit conversion expressions with accessibility |
| [`is_bitwise_trivially_relocatable`](is_bitwise_trivially_relocatable.md) | Specifies whether a type is bitwise trivially relocatable |
| [`not_implemented` ](not_implemented.md) | Exception thrown by `weak_dispatch` for the default implementation |
| [`operator_dispatch`](operator_dispatch/README.md) | Dispatch type for operator expressions with accessibility |
| [`proxy_indirect_accessor`](proxy_indirect_accessor.md) | Provides indirection accessibility for `proxy` |
Expand Down
10 changes: 5 additions & 5 deletions docs/spec/allocate_proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
> Module: `proxy`
> Namespace: `pro::inline v4`

The definition of `allocate_proxy` makes use of an exposition-only class template *allocated-ptr*. An object of type `allocated-ptr<T, Alloc>` allocates the storage for another object of type `T` with an allocator of type `Alloc` and manages the lifetime of this contained object. Similar to [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional), `allocated-ptr<T, Alloc>` provides `operator*` for accessing the managed object of type `T` with the same qualifiers, but does not necessarily support the state where the contained object is absent.
The definition of `allocate_proxy` makes use of an exposition-only class template *allocated-ptr*. An object of type *allocated-ptr&lt;T, Alloc&gt;* allocates the storage for another object of type `T` with an allocator of type `Alloc` and manages the lifetime of this contained object. Similar to [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional), *allocated-ptr&lt;T, Alloc&gt;* provides `operator*` for accessing the managed object of type `T` with the same qualifiers, but does not necessarily support the state where the contained object is absent.

```cpp
// (1)
Expand All @@ -20,11 +20,11 @@ template <facade F, class Alloc, class T>
proxy<F> allocate_proxy(const Alloc& alloc, T&& value); // freestanding-deleted
```

`(1)` Creates a `proxy<F>` object containing a value `p` of type `allocated-ptr<T, Alloc>`, where `*p` is direct-non-list-initialized with `std::forward<Args>(args)...`.
`(1)` Creates a `proxy<F>` object containing a value `p` of type *allocated-ptr&lt;T, Alloc&gt;*, where `*p` is direct-non-list-initialized with `std::forward<Args>(args)...`.

`(2)` Creates a `proxy<F>` object containing a value `p` of type `allocated-ptr<T, Alloc>`, where `*p` is direct-non-list-initialized with `il, std::forward<Args>(args)...`.
`(2)` Creates a `proxy<F>` object containing a value `p` of type *allocated-ptr&lt;T, Alloc&gt;*, where `*p` is direct-non-list-initialized with `il, std::forward<Args>(args)...`.

`(3)` Creates a `proxy<F>` object containing a value `p` of type `allocated-ptr<std::decay_t<T>, Alloc>`, where `*p` is direct-non-list-initialized with `std::forward<T>(value)`.
`(3)` Creates a `proxy<F>` object containing a value `p` of type *allocated-ptr&lt;*`std::decay_t<T>`*, Alloc&gt;*, where `*p` is direct-non-list-initialized with `std::forward<T>(value)`.

*Since 3.3.0*: For `(1-3)`, if [`proxiable_target<std::decay_t<T>, F>`](proxiable_target.md) is `false`, the program is ill-formed and diagnostic messages are generated.

Expand All @@ -38,7 +38,7 @@ Throws any exception thrown by allocation or the constructor of `T`.

## Notes

The implementation of `allocated-ptr` may vary depending on the definition of `F`. Specifically, when `F::max_size` and `F::max_align` are not large enough to hold both a pointer to the allocated memory and a copy of the allocator, `allocated-ptr` shall allocate additional storage for the allocator.
The implementation of *allocated-ptr* may vary depending on the definition of `F`. Specifically, when `F::max_size` and `F::max_align` are not large enough to hold both a pointer to the allocated memory and a copy of the allocator, *allocated-ptr* shall allocate additional storage for the allocator.

## Example

Expand Down
10 changes: 5 additions & 5 deletions docs/spec/allocate_proxy_shared.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
> Namespace: `pro::inline v4`
> Since: 3.3.0

The definition of `allocate_proxy_shared` makes use of exposition-only class templates *strong-compact-ptr* and *weak-compact-ptr*. Their semantics are similar to [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr) and [`std::weak_ptr`](https://en.cppreference.com/w/cpp/memory/weak_ptr), but do not provide a polymorphic deleter. Their size and alignment are guaranteed not to be greater than those of a raw pointer type. `strong-compact-ptr<T, Alloc>` is conditionally convertible to `weak-compact-ptr<T, Alloc>` only if necessary. Similar to [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional), `strong-compact-ptr<T, Alloc>` provides `operator*` for accessing the managed object of type `T` with the same qualifiers.
The definition of `allocate_proxy_shared` makes use of exposition-only class templates *strong-compact-ptr* and *weak-compact-ptr*. Their semantics are similar to [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr) and [`std::weak_ptr`](https://en.cppreference.com/w/cpp/memory/weak_ptr), but do not provide a polymorphic deleter. Their size and alignment are guaranteed not to be greater than those of a raw pointer type. *strong-compact-ptr&lt;T, Alloc&gt;* is conditionally convertible to *weak-compact-ptr&lt;T, Alloc&gt;* only if necessary. Similar to [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional), *strong-compact-ptr&lt;T, Alloc&gt;* provides `operator*` for accessing the managed object of type `T` with the same qualifiers.

```cpp
// (1)
Expand All @@ -21,11 +21,11 @@ template <facade F, class Alloc, class T>
proxy<F> allocate_proxy_shared(const Alloc& alloc, T&& value); // freestanding-deleted
```

`(1)` Creates a `proxy<F>` object containing a value `p` of type `strong-compact-ptr<T, Alloc>`, where `*p` is direct-non-list-initialized with `std::forward<Args>(args)...`.
`(1)` Creates a `proxy<F>` object containing a value `p` of type *strong-compact-ptr&lt;T, Alloc&gt;*, where `*p` is direct-non-list-initialized with `std::forward<Args>(args)...`.

`(2)` Creates a `proxy<F>` object containing a value `p` of type `strong-compact-ptr<T, Alloc>`, where `*p` is direct-non-list-initialized with `il, std::forward<Args>(args)...`.
`(2)` Creates a `proxy<F>` object containing a value `p` of type *strong-compact-ptr&lt;T, Alloc&gt;*, where `*p` is direct-non-list-initialized with `il, std::forward<Args>(args)...`.

`(3)` Creates a `proxy<F>` object containing a value `p` of type `strong-compact-ptr<std::decay_t<T>, Alloc>`, where `*p` is direct-non-list-initialized with `std::forward<T>(value)`.
`(3)` Creates a `proxy<F>` object containing a value `p` of type *strong-compact-ptr&lt;*`std::decay_t<T>`*, Alloc&gt;*, where `*p` is direct-non-list-initialized with `std::forward<T>(value)`.

For `(1-3)`, if [`proxiable_target<std::decay_t<T>, F>`](proxiable_target.md) is `false`, the program is ill-formed and diagnostic messages are generated.

Expand All @@ -39,7 +39,7 @@ Throws any exception thrown by allocation or the constructor of `T`.

## Notes

The implementation of `strong-compact-ptr` may vary depending on the definition of `F`. Specifically, when `F` does not support weak ownership via [`skills::as_weak`](skills_as_weak.md), `strong-compact-ptr<T, Alloc>` is not convertible to `weak-compact-ptr<T, Alloc>`, which leaves more room for optimization.
The implementation of *strong-compact-ptr* may vary depending on the definition of `F`. Specifically, when `F` does not support weak ownership via [`skills::as_weak`](skills_as_weak.md), *strong-compact-ptr&lt;T, Alloc&gt;* is not convertible to *strong-compact-ptr&lt;T, Alloc&gt;*, which leaves more room for optimization.

## Example

Expand Down
103 changes: 103 additions & 0 deletions docs/spec/is_bitwise_trivially_relocatable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Class template `is_bitwise_trivially_relocatable`

> Header: `proxy.h`
> Module: `proxy`
> Namespace: `pro::inline v4`
> Since: 4.0.0

```cpp
template <class T>
struct is_bitwise_trivially_relocatable;

template <class T>
constexpr bool is_bitwise_trivially_relocatable_v =
is_bitwise_trivially_relocatable<T>::value;
```

The class template `is_bitwise_trivially_relocatable<T>` is a type trait whose `value` is `true` when objects of (complete) type `T` can be *bitwise trivially relocated*: a new object of type `T` can be created at an arbitrary suitably aligned storage location by performing a raw byte-wise copy (as if by `std::memcpy`) of `sizeof(T)` bytes from the original object's storage, and the original object can then be considered destroyed (its lifetime ends) without invoking its destructor. Otherwise the `value` is `false`.

Semantics follow the model described in [P3780R0](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3780r0.html), not the `std::trivially_relocatable` facility from [P2786R13](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html) (C++26). The library keeps this separate trait for portability and because its internal optimizations rely on the "memcpy relocation" guarantee.

## Definition

The primary template is defined as:

```cpp
template <class T>
struct is_bitwise_trivially_relocatable
: std::bool_constant<std::is_trivially_move_constructible_v<T> &&
std::is_trivially_destructible_v<T>> {};
```

Thus, by default, any type that is both trivially move constructible and trivially destructible is treated as bitwise trivially relocatable. Users may explicitly specialize the trait to `std::true_type` for additional types that meet the semantic requirement even if they are not both trivially move constructible and trivially destructible (e.g., certain pointer-like wrapper types). Users must not specialize it to `false` when the primary template would yield `true`.

In addition to the primary template, the implementation provides (positive) specializations for types that are known to satisfy the bitwise trivial relocation property:

- `std::unique_ptr<T, D>` when `D` is bitwise trivially relocatable
- `std::shared_ptr<T>`
- `std::weak_ptr<T>`
- *inplace-ptr&lt;T&gt;* when `T` is bitwise trivially relocatable (see [function template `make_proxy_inplace`](make_proxy_inplace.md))
- *allocated-ptr&lt;T, Alloc&gt;* (see [function template `allocate_proxy`](allocate_proxy.md))
- *strong-compact-ptr&lt;T, Alloc&gt;* (see [function template `allocate_proxy_shared`](allocate_proxy_shared.md))
- *weak-compact-ptr&lt;T, Alloc&gt;* (see [function template `allocate_proxy_shared`](allocate_proxy_shared.md))

These specializations reflect empirical knowledge of the representations of common "fancy pointer" types: relocating them with a raw byte copy preserves their invariants, and skipping destructor invocation of the source object has no observable effect beyond finalization already accounted for in the target representation.

## Notes

### Relationship to `std::trivially_relocatable`

C++26 `std::trivially_relocatable` may encompass additional nuances (it can be satisfied by types whose relocation uses compiler transformations beyond an as-if byte copy). A type for which this trait is `true` is always a good candidate to be `std::trivially_relocatable`, but the converse is not required by this library.

### Customization Guidelines

You may provide additional specializations of `is_bitwise_trivially_relocatable<T>` (in namespace `pro`) to opt in types you own. A correct specialization must ensure the type does not depend on its *address* remaining stable (self-pointers, intrusive container hooks, pointer provenance, etc.).

A positive specialization is a promise you must uphold. Violating the contract results in undefined behavior in any operation that uses the fast relocation path (e.g., certain `proxy` conversions or assignments).

## Example

```cpp
#include <type_traits>

#include <proxy/proxy.h>

struct Any : pro::facade_builder //
::build {}; // Requires trivial relocatability by default

struct A {
int Val;
};

struct B {
B() = default;
B(B&&) noexcept {}

int Val;
};

struct C {
C() = default;
C(B&&) noexcept {}

int Val;
};

namespace pro {

template <>
struct is_bitwise_trivially_relocatable<C> : std::true_type {};

} // namespace pro

int main() {
static_assert(pro::inplace_proxiable_target<A, Any>);
static_assert(!pro::inplace_proxiable_target<B, Any>);
static_assert(pro::inplace_proxiable_target<C, Any>);
}
```

## See Also

- [named requirements *ProFacade*](ProFacade.md)
- [`basic_facade_builder::support_relocation`](basic_facade_builder/support_relocation.md)
8 changes: 4 additions & 4 deletions docs/spec/make_proxy_inplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
> Module: `proxy`
> Namespace: `pro::inline v4`

The definition of `make_proxy_inplace` makes use of an exposition-only class template *inplace-ptr*. Similar to [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional), `inplace-ptr<T>` contains the storage for an object of type `T`, manages its lifetime, and provides `operator*` for access with the same qualifiers. However, it does not necessarily support the state where the contained object is absent. `inplace-ptr<T>` has the same size and alignment as `T`.
The definition of `make_proxy_inplace` makes use of an exposition-only class template *inplace-ptr*. Similar to [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional), *inplace-ptr&lt;T&gt;* contains the storage for an object of type `T`, manages its lifetime, and provides `operator*` for access with the same qualifiers. However, it does not necessarily support the state where the contained object is absent. *inplace-ptr&lt;T&gt;* has the same size and alignment as `T`.

```cpp
// (1)
Expand All @@ -27,11 +27,11 @@ proxy<F> make_proxy_inplace(T&& value)
requires(std::is_constructible_v<std::decay_t<T>, T>);
```

`(1)` Creates a `proxy<F>` object containing a value `p` of type `inplace-ptr<T>`, where `*p` is direct-non-list-initialized with `std::forward<Args>(args)...`.
`(1)` Creates a `proxy<F>` object containing a value `p` of type *inplace-ptr&lt;T&gt;*, where `*p` is direct-non-list-initialized with `std::forward<Args>(args)...`.

`(2)` Creates a `proxy<F>` object containing a value `p` of type `inplace-ptr<T>`, where `*p` is direct-non-list-initialized with `il, std::forward<Args>(args)...`.
`(2)` Creates a `proxy<F>` object containing a value `p` of type *inplace-ptr&lt;T&gt;*, where `*p` is direct-non-list-initialized with `il, std::forward<Args>(args)...`.

`(3)` Creates a `proxy<F>` object containing a value `p` of type `inplace-ptr<std::decay_t<T>>`, where `*p` is direct-non-list-initialized with `std::forward<T>(value)`.
`(3)` Creates a `proxy<F>` object containing a value `p` of type *inplace-ptr&lt;*`std::decay_t`*&gt;*, where `*p` is direct-non-list-initialized with `std::forward<T>(value)`.

*Since 3.3.0*: For `(1-3)`, if [`inplace_proxiable_target<std::decay_t<T>, F>`](inplace_proxiable_target.md) is `false`, the program is ill-formed and diagnostic messages are generated.

Expand Down
Loading