From 91eee30dac995aff065e43cb4318525586e29448 Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Thu, 20 Jun 2024 10:17:39 +0800
Subject: [PATCH 1/5] Implement indirection

---
 README.md                                     |   16 +-
 proxy.h                                       | 1206 ++++++++++++-----
 samples/resource_dictionary/main.cpp          |    2 +-
 .../freestanding/proxy_freestanding_tests.cpp |    8 +-
 tests/proxy_creation_tests.cpp                |   61 +-
 tests/proxy_dispatch_tests.cpp                |  224 +--
 tests/proxy_integration_tests.cpp             |    8 +-
 tests/proxy_invocation_tests.cpp              |   88 +-
 tests/proxy_lifetime_tests.cpp                |   62 +-
 tests/proxy_reflection_tests.cpp              |   14 +-
 tests/proxy_traits_tests.cpp                  |   12 +-
 11 files changed, 1097 insertions(+), 604 deletions(-)

diff --git a/README.md b/README.md
index 6594e5f9..c8ba2c1a 100644
--- a/README.md
+++ b/README.md
@@ -48,8 +48,8 @@ class Rectangle {
 std::string PrintDrawableToString(pro::proxy<Drawable> p) {
   std::stringstream result;
   result << "shape = ";
-  p.Draw(result);  // Polymorphic call
-  result << ", area = " << p.Area();  // Polymorphic call
+  p->Draw(result);  // Polymorphic call
+  result << ", area = " << p->Area();  // Polymorphic call
   return std::move(result).str();
 }
 
@@ -76,11 +76,11 @@ struct Logger : pro::facade_builder
 
 // Client - Consumer
 void MyVerboseFunction(pro::proxy<Logger> logger) {
-  logger.Log("hello");
+  logger->Log("hello");
   try {
     throw std::runtime_error{"runtime error!"};
   } catch (const std::exception& e) {
-    logger.Log("world", e);
+    logger->Log("world", e);
   }
 }
 
@@ -133,11 +133,11 @@ int main() {
     puts("");
   };
   MyFunction<void(int)> p0{&f};
-  p0(123);  // Prints "f() called. Args: 123:i," (assuming GCC)
+  (*p0)(123);  // Prints "f() called. Args: 123:i," (assuming GCC)
   MyMoveOnlyFunction<void(), void(int), void(double)> p1{&f};
-  p1();  // Prints "f() called. Args:"
-  p1(456);  // Prints "f() called. Args: 456:i,"
-  p1(1.2);  // Prints "f() called. Args: 1.2:d,"
+  (*p1)();  // Prints "f() called. Args:"
+  (*p1)(456);  // Prints "f() called. Args: 456:i,"
+  (*p1)(1.2);  // Prints "f() called. Args: 1.2:d,"
   return 0;
 }
 ```
diff --git a/proxy.h b/proxy.h
index de470900..d9c8df18 100644
--- a/proxy.h
+++ b/proxy.h
@@ -13,6 +13,18 @@
 #include <type_traits>
 #include <utility>
 
+#if __has_cpp_attribute(msvc::no_unique_address)
+#define ___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE msvc::no_unique_address
+#elif __has_cpp_attribute(no_unique_address)
+#define ___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE no_unique_address
+#else
+#error "Proxy requires C++20 attribute no_unique_address"
+#endif
+
+#define ___PRO_DIRECT_FUNC_IMPL(...) \
+    noexcept(noexcept(__VA_ARGS__)) requires(requires { __VA_ARGS__; }) \
+    { return __VA_ARGS__; }
+
 namespace pro {
 
 enum class constraint_level { none, nontrivial, nothrow, trivial };
@@ -38,6 +50,38 @@ template <class T, class...> struct lazy_eval_traits : std::type_identity<T> {};
 template <class U, class... T>
 using lazy_eval_t = typename lazy_eval_traits<U, T...>::type;
 
+enum class qualifier_type { lv, const_lv, rv, const_rv };
+
+template <class T> struct qualifier_of_traits;
+template <class T>
+struct qualifier_of_traits<T&>
+    : std::integral_constant<qualifier_type, qualifier_type::lv> {};
+template <class T>
+struct qualifier_of_traits<const T&>
+    : std::integral_constant<qualifier_type, qualifier_type::const_lv> {};
+template <class T>
+struct qualifier_of_traits<T&&>
+    : std::integral_constant<qualifier_type, qualifier_type::rv> {};
+template <class T>
+struct qualifier_of_traits<const T&&>
+    : std::integral_constant<qualifier_type, qualifier_type::const_rv> {};
+template <class T>
+constexpr qualifier_type qualifier_of_v = qualifier_of_traits<T>::value;
+
+template <class T, qualifier_type Q> struct add_qualifier_traits;
+template <class T>
+struct add_qualifier_traits<T, qualifier_type::lv> : std::type_identity<T&> {};
+template <class T>
+struct add_qualifier_traits<T, qualifier_type::const_lv>
+    : std::type_identity<const T&> {};
+template <class T>
+struct add_qualifier_traits<T, qualifier_type::rv> : std::type_identity<T&&> {};
+template <class T>
+struct add_qualifier_traits<T, qualifier_type::const_rv>
+    : std::type_identity<const T&&> {};
+template <class T, qualifier_type Q>
+using add_qualifier_t = typename add_qualifier_traits<T, Q>::type;
+
 template <template <class, class> class R, class O, class... Is>
 struct recursive_reduction : std::type_identity<O> {};
 template <template <class, class> class R, class O, class... Is>
@@ -143,9 +187,12 @@ constexpr bool invocable_dispatch = std::conditional_t<
     NE, std::is_nothrow_invocable_r<R, D, Args...>,
     std::is_invocable_r<R, D, Args...>>::value;
 template <class D, class P, bool NE, class R, class... Args>
-concept invocable_dispatch_ptr = invocable_dispatch<
+concept invocable_indirect_dispatch_ptr = invocable_dispatch<
     D, NE, R, typename ptr_traits<P>::target_type&, Args...> ||
-    invocable_dispatch<D, NE, R, const P&, Args...> ||
+    invocable_dispatch<D, NE, R, std::nullptr_t, Args...>;
+template <class D, class P, qualifier_type Q, bool NE, class R, class... Args>
+concept invocable_direct_dispatch_ptr = invocable_dispatch<
+    D, NE, R, add_qualifier_t<P, Q>, Args...> ||
     invocable_dispatch<D, NE, R, std::nullptr_t, Args...>;
 
 template <bool NE, class R, class... Args>
@@ -161,85 +208,157 @@ R invoke_dispatch(Args&&... args) {
   }
 }
 template <class P, class D, class R, class... Args>
-R invocation_dispatcher_ref(const std::byte* self, Args... args)
+R indirect_conv_dispatcher(const std::byte& self, Args... args)
     noexcept(invocable_dispatch<
         D, true, R, typename ptr_traits<P>::target_type&, Args...>) {
   return invoke_dispatch<D, R>(ptr_traits<P>::dereference(*std::launder(
-      reinterpret_cast<const P*>(self))), std::forward<Args>(args)...);
+      reinterpret_cast<const P*>(&self))), std::forward<Args>(args)...);
 }
-template <class P, class D, class R, class... Args>
-R invocation_dispatcher_ptr(const std::byte* self, Args... args)
-    noexcept(invocable_dispatch<D, true, R, const P&, Args...>) {
-  return invoke_dispatch<D, R>(*std::launder(reinterpret_cast<const P*>(self)),
-      std::forward<Args>(args)...);
+template <class P, class D, qualifier_type Q, class R, class... Args>
+R direct_conv_dispatcher(add_qualifier_t<std::byte, Q> self, Args... args)
+    noexcept(invocable_dispatch<
+        D, true, R, typename ptr_traits<P>::target_type&, Args...>) {
+  using Ptr = add_qualifier_t<P, Q>;
+  return invoke_dispatch<D, R>(std::forward<Ptr>(*std::launder(reinterpret_cast<
+      std::remove_reference_t<Ptr>*>(&self))), std::forward<Args>(args)...);
 }
-template <class D, class R, class... Args>
-R invocation_dispatcher_default(const std::byte*, Args... args)
+template <class D, qualifier_type Q, class R, class... Args>
+R default_conv_dispatcher(add_qualifier_t<std::byte, Q>, Args... args)
     noexcept(invocable_dispatch<D, true, R, std::nullptr_t, Args...>)
     { return invoke_dispatch<D, R>(nullptr, std::forward<Args>(args)...); }
 template <class P>
-void copying_dispatcher(std::byte* self, const std::byte* rhs)
+void copying_dispatcher(std::byte& self, const std::byte& rhs)
     noexcept(has_copyability<P>(constraint_level::nothrow)) {
-  std::construct_at(reinterpret_cast<P*>(self),
-      *std::launder(reinterpret_cast<const P*>(rhs)));
+  std::construct_at(reinterpret_cast<P*>(&self),
+      *std::launder(reinterpret_cast<const P*>(&rhs)));
 }
 template <std::size_t Len, std::size_t Align>
-void copying_default_dispatcher(std::byte* self, const std::byte* rhs)
+void copying_default_dispatcher(std::byte& self, const std::byte& rhs)
     noexcept {
   std::uninitialized_copy_n(
-      std::assume_aligned<Align>(rhs), Len, std::assume_aligned<Align>(self));
+      std::assume_aligned<Align>(&rhs), Len, std::assume_aligned<Align>(&self));
 }
 template <class P>
-void relocation_dispatcher(std::byte* self, const std::byte* rhs)
+void relocation_dispatcher(std::byte& self, const std::byte& rhs)
     noexcept(has_relocatability<P>(constraint_level::nothrow)) {
-  P* other = std::launder(reinterpret_cast<P*>(const_cast<std::byte*>(rhs)));
-  std::construct_at(reinterpret_cast<P*>(self), std::move(*other));
+  P* other = std::launder(reinterpret_cast<P*>(const_cast<std::byte*>(&rhs)));
+  std::construct_at(reinterpret_cast<P*>(&self), std::move(*other));
   std::destroy_at(other);
 }
 template <class P>
-void destruction_dispatcher(std::byte* self)
+void destruction_dispatcher(std::byte& self)
     noexcept(has_destructibility<P>(constraint_level::nothrow))
-    { std::destroy_at(std::launder(reinterpret_cast<P*>(self))); }
-inline void destruction_default_dispatcher(std::byte*) noexcept {}
+    { std::destroy_at(std::launder(reinterpret_cast<P*>(&self))); }
+inline void destruction_default_dispatcher(std::byte&) noexcept {}
 
+template <bool IS_DIRECT, class O>
+struct overload_traits : inapplicable_traits {};
 template <bool NE, class R, class... Args>
-struct overload_traits_impl : applicable_traits {
-  using dispatcher_type = func_ptr_t<NE, R, const std::byte*, Args...>;
+struct indirect_overload_traits_impl : applicable_traits {
+  using dispatcher_type = func_ptr_t<NE, R, const std::byte&, Args...>;
   template <class D>
   struct meta_provider {
     template <class P>
     static constexpr dispatcher_type get() {
       if constexpr (invocable_dispatch<
           D, NE, R, typename ptr_traits<P>::target_type&, Args...>) {
-        return &invocation_dispatcher_ref<P, D, R, Args...>;
-      } else if constexpr (invocable_dispatch<D, NE, R, const P&, Args...>) {
-        return &invocation_dispatcher_ptr<P, D, R, Args...>;
+        return &indirect_conv_dispatcher<P, D, R, Args...>;
       } else {
-        return &invocation_dispatcher_default<D, R, Args...>;
+        return &default_conv_dispatcher<
+            D, qualifier_type::const_lv, R, Args...>;
       }
     }
   };
-  struct resolver { func_ptr_t<NE, R, Args...> operator()(Args...); };
+  struct resolver
+      { indirect_overload_traits_impl operator()(const std::byte&, Args...); };
   template <class D, class P>
   static constexpr bool applicable_ptr =
-      invocable_dispatch_ptr<D, P, NE, R, Args...>;
+      invocable_indirect_dispatch_ptr<D, P, NE, R, Args...>;
   static constexpr bool is_noexcept = NE;
-  template <class... Args2>
-  static constexpr bool matches = std::is_invocable_v<resolver, Args2...>;
 };
-template <class O> struct overload_traits : inapplicable_traits {};
+template <qualifier_type Q, bool NE, class R, class... Args>
+struct direct_overload_traits_impl : applicable_traits {
+  using dispatcher_type = func_ptr_t<
+      NE, R, add_qualifier_t<std::byte, Q>, Args...>;
+  template <class D>
+  struct meta_provider {
+    template <class P>
+    static constexpr dispatcher_type get() {
+      if constexpr (invocable_dispatch<
+          D, NE, R, add_qualifier_t<P, Q>, Args...>) {
+        return &direct_conv_dispatcher<P, D, Q, R, Args...>;
+      } else {
+        return &default_conv_dispatcher<D, Q, R, Args...>;
+      }
+    }
+  };
+  struct resolver {
+    direct_overload_traits_impl operator()(
+        add_qualifier_t<std::byte, Q>, Args...);
+  };
+  template <class D, class P>
+  static constexpr bool applicable_ptr =
+      invocable_direct_dispatch_ptr<D, P, Q, NE, R, Args...>;
+  static constexpr bool is_noexcept = NE;
+};
+template <class R, class... Args>
+struct overload_traits<false, R(Args...)>
+    : indirect_overload_traits_impl<false, R, Args...> {};
+template <class R, class... Args>
+struct overload_traits<false, R(Args...) noexcept>
+    : indirect_overload_traits_impl<true, R, Args...> {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...)>
+    : direct_overload_traits_impl<qualifier_type::lv, false, R, Args...> {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) noexcept>
+    : direct_overload_traits_impl<qualifier_type::lv, true, R, Args...> {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) &>
+    : direct_overload_traits_impl<qualifier_type::lv, false, R, Args...> {};
 template <class R, class... Args>
-struct overload_traits<R(Args...)> : overload_traits_impl<false, R, Args...> {};
+struct overload_traits<true, R(Args...) & noexcept>
+    : direct_overload_traits_impl<qualifier_type::lv, true, R, Args...> {};
 template <class R, class... Args>
-struct overload_traits<R(Args...) noexcept>
-    : overload_traits_impl<true, R, Args...> {};
+struct overload_traits<true, R(Args...) &&>
+    : direct_overload_traits_impl<qualifier_type::rv, false, R, Args...> {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) && noexcept>
+    : direct_overload_traits_impl<qualifier_type::rv, true, R, Args...> {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) const>
+    : direct_overload_traits_impl<qualifier_type::const_lv, false, R, Args...>
+    {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) const noexcept>
+    : direct_overload_traits_impl<qualifier_type::const_lv, true, R, Args...>
+    {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) const&>
+    : direct_overload_traits_impl<qualifier_type::const_lv, false, R, Args...>
+    {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) const& noexcept>
+    : direct_overload_traits_impl<qualifier_type::const_lv, true, R, Args...>
+    {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) const&&>
+    : direct_overload_traits_impl<qualifier_type::const_rv, false, R, Args...>
+    {};
+template <class R, class... Args>
+struct overload_traits<true, R(Args...) const&& noexcept>
+    : direct_overload_traits_impl<qualifier_type::const_rv, true, R, Args...>
+    {};
 
+template <class T> struct nullable_traits : inapplicable_traits {};
 template <class T>
-struct nullable_traits : conditional_traits<requires(const T& cv, T& v) {
-      { T{} } noexcept;
-      { cv.has_value() } noexcept -> std::same_as<bool>;
-      { v.reset() } noexcept;
-    }> {};
+    requires(
+        requires(const T& cv, T& v) {
+          { T{} } noexcept;
+          { cv.has_value() } noexcept -> std::same_as<bool>;
+          { v.reset() } noexcept;
+        })
+struct nullable_traits<T> : applicable_traits {};
 
 template <class MP>
 struct dispatcher_meta {
@@ -272,35 +391,63 @@ template <class O, class I> struct meta_reduction : std::type_identity<O> {};
 template <class... Ms, class I> requires(!std::is_void_v<I>)
 struct meta_reduction<composite_meta_impl<Ms...>, I>
     : std::type_identity<composite_meta_impl<Ms..., I>> {};
-template <class... Ms0, class... Ms1>
-struct meta_reduction<composite_meta_impl<Ms0...>, composite_meta_impl<Ms1...>>
-    : std::type_identity<composite_meta_impl<Ms0..., Ms1...>> {};
+template <class... Ms1, class... Ms2>
+struct meta_reduction<composite_meta_impl<Ms1...>, composite_meta_impl<Ms2...>>
+    : std::type_identity<composite_meta_impl<Ms1..., Ms2...>> {};
 template <class O, class I>
 using meta_reduction_t = typename meta_reduction<O, I>::type;
 template <class... Ms>
 using composite_meta =
     recursive_reduction_t<meta_reduction_t, composite_meta_impl<>, Ms...>;
 
+template <class T, class F>
+using proxy_accessor = typename T::template accessor<proxy<F>>;
+template <class T>
+consteval bool is_meta_is_direct_well_formed() {
+  if constexpr (requires { { T::is_direct } -> std::same_as<const bool&>; }) {
+    if constexpr (is_consteval([] { return T::is_direct; })) {
+      return true;
+    }
+  }
+  return false;
+}
+template <class T, class F>
+consteval bool is_a11y_well_formed() {
+  if constexpr (requires { typename proxy_accessor<T, F>; }) {
+    return !std::is_final_v<proxy_accessor<T, F>> &&
+        std::is_nothrow_default_constructible_v<proxy_accessor<T, F>>;
+  } else {
+    return true;
+  }
+  return false;
+}
+
 template <class C, class... Os>
 struct conv_traits_impl : inapplicable_traits {};
 template <class C, class... Os>
-    requires(sizeof...(Os) > 0u && (overload_traits<Os>::applicable && ...))
+    requires(sizeof...(Os) > 0u && (overload_traits<
+        C::dispatch_type::is_direct, Os>::applicable && ...))
 struct conv_traits_impl<C, Os...> : applicable_traits {
  private:
-  struct overload_resolver : overload_traits<Os>::resolver...
-      { using overload_traits<Os>::resolver::operator()...; };
+  struct overload_resolver : overload_traits<
+      C::dispatch_type::is_direct, Os>::resolver... {
+    using overload_traits<C::dispatch_type::is_direct, Os>::resolver
+        ::operator()...;
+  };
 
  public:
   using dispatch_type = typename C::dispatch_type;
-  using meta = composite_meta<dispatcher_meta<
-      typename overload_traits<Os>::template meta_provider<dispatch_type>>...>;
+  using meta = composite_meta_impl<dispatcher_meta<
+      typename overload_traits<C::dispatch_type::is_direct, Os>
+          ::template meta_provider<dispatch_type>>...>;
   template <class... Args>
-  using matched_overload =
-      std::remove_pointer_t<std::invoke_result_t<overload_resolver, Args...>>;
+  using matched_overload_traits =
+      std::invoke_result_t<overload_resolver, Args...>;
 
   template <class P>
-  static constexpr bool applicable_ptr =
-      (overload_traits<Os>::template applicable_ptr<dispatch_type, P> && ...);
+  static constexpr bool applicable_ptr = (overload_traits<
+      C::dispatch_type::is_direct, Os>
+          ::template applicable_ptr<dispatch_type, P> && ...);
 };
 template <class C> struct conv_traits : inapplicable_traits {};
 template <class C>
@@ -310,14 +457,29 @@ template <class C>
           typename C::overload_types;
         } &&
         std::is_nothrow_default_constructible_v<typename C::dispatch_type> &&
+        is_meta_is_direct_well_formed<typename C::dispatch_type>() &&
         is_tuple_like_well_formed<typename C::overload_types>())
-struct conv_traits<C> : instantiated_t<
-    conv_traits_impl, typename C::overload_types, C> {};
+struct conv_traits<C>
+    : instantiated_t<conv_traits_impl, typename C::overload_types, C> {};
+
+template <class R>
+struct indirect_refl_meta : R {
+  constexpr indirect_refl_meta()
+      noexcept(std::is_nothrow_default_constructible_v<R>)
+      requires(std::is_default_constructible_v<R>) = default;
+  template <class P>
+  constexpr explicit indirect_refl_meta(std::in_place_type_t<P>)
+      noexcept(std::is_nothrow_constructible_v<
+          R, std::in_place_type_t<typename ptr_traits<P>::target_type>>)
+      requires(std::is_constructible_v<
+          R, std::in_place_type_t<typename ptr_traits<P>::target_type>>)
+      : R(std::in_place_type<typename ptr_traits<P>::target_type>) {}
+};
 
 template <bool NE>
 struct copyability_meta_provider {
   template <class P>
-  static constexpr func_ptr_t<NE, void, std::byte*, const std::byte*> get() {
+  static constexpr func_ptr_t<NE, void, std::byte&, const std::byte&> get() {
     if constexpr (has_copyability<P>(constraint_level::trivial)) {
       return &copying_default_dispatcher<sizeof(P), alignof(P)>;
     } else {
@@ -328,7 +490,7 @@ struct copyability_meta_provider {
 template <bool NE>
 struct relocatability_meta_provider {
   template <class P>
-  static constexpr func_ptr_t<NE, void, std::byte*, const std::byte*> get() {
+  static constexpr func_ptr_t<NE, void, std::byte&, const std::byte&> get() {
     if constexpr (has_relocatability<P>(constraint_level::trivial)) {
       return &copying_default_dispatcher<sizeof(P), alignof(P)>;
     } else {
@@ -339,7 +501,7 @@ struct relocatability_meta_provider {
 template <bool NE>
 struct destructibility_meta_provider {
   template <class P>
-  static constexpr func_ptr_t<NE, void, std::byte*> get() {
+  static constexpr func_ptr_t<NE, void, std::byte&> get() {
     if constexpr (has_destructibility<P>(constraint_level::trivial)) {
       return &destruction_default_dispatcher;
     } else {
@@ -358,28 +520,42 @@ struct lifetime_meta_traits<MP, constraint_level::nontrivial>
 template <template <bool> class MP, constraint_level C>
 using lifetime_meta_t = typename lifetime_meta_traits<MP, C>::type;
 
-template <class... As> struct composite_accessor_impl : As... {};
-template <class T, class O, class I>
-struct accessor_reduction : std::type_identity<O> {};
-template <class T, class... As, class I>
-    requires(requires { typename I::template accessor<T>; } &&
-        std::is_nothrow_default_constructible_v<
-            typename I::template accessor<T>>)
-struct accessor_reduction<T, composite_accessor_impl<As...>, I>
+template <class... As>
+class composite_accessor_impl : public As... {
+  template <class> friend class pro::proxy;
+
+  composite_accessor_impl() noexcept = default;
+  composite_accessor_impl(const composite_accessor_impl&) noexcept = default;
+  composite_accessor_impl& operator=(const composite_accessor_impl&) noexcept
+      = default;
+};
+template <class F, bool IS_DIRECT, class O, class I>
+struct composite_accessor_reduction : std::type_identity<O> {};
+template <class F, bool IS_DIRECT, class... As, class I>
+    requires((I::is_direct == IS_DIRECT) &&
+        requires { typename proxy_accessor<I, F>; })
+struct composite_accessor_reduction<
+    F, IS_DIRECT, composite_accessor_impl<As...>, I>
     : std::type_identity<composite_accessor_impl<
-          As..., typename I::template accessor<T>>> {};
-template <class T, class... As0, class... As1>
-struct accessor_reduction<
-    T, composite_accessor_impl<As0...>, composite_accessor_impl<As1...>>
-    : std::type_identity<composite_accessor_impl<As0..., As1...>> {};
-template <class T>
-struct accessor_helper {
+          As..., proxy_accessor<I, F>>> {};
+template <class F, bool IS_DIRECT>
+struct composite_accessor_helper {
   template <class O, class I>
-  using reduction_t = typename accessor_reduction<T, O, I>::type;
+  using reduction_t =
+      typename composite_accessor_reduction<F, IS_DIRECT, O, I>::type;
 };
-template <class F, class... As>
-using composite_accessor = recursive_reduction_t<accessor_helper<proxy<F>>
-    ::template reduction_t, composite_accessor_impl<>, As...>;
+template <class F, bool IS_DIRECT, class... Ts>
+using composite_accessor = recursive_reduction_t<composite_accessor_helper<
+    F, IS_DIRECT>::template reduction_t, composite_accessor_impl<>, Ts...>;
+
+template <class As1, class As2> struct merge_composite_accessor_traits;
+template <class... As1, class... As2>
+struct merge_composite_accessor_traits<
+    composite_accessor_impl<As1...>, composite_accessor_impl<As2...>>
+    : std::type_identity<composite_accessor_impl<As1..., As2...>> {};
+template <class T, class U>
+using merged_composite_accessor =
+    typename merge_composite_accessor_traits<T, U>::type;
 
 template <class F>
 consteval bool is_facade_constraints_well_formed() {
@@ -394,10 +570,12 @@ consteval bool is_facade_constraints_well_formed() {
 }
 template <class R, class P>
 consteval bool is_reflection_type_well_formed() {
-  if constexpr (std::is_void_v<R>) {
-    return true;
-  } else if constexpr (std::is_constructible_v<R, std::in_place_type_t<P>>) {
-    return is_consteval([] { return R{std::in_place_type<P>}; });
+  using T = std::conditional_t<R::is_direct, P,
+      typename ptr_traits<P>::target_type>;
+  if constexpr (std::is_constructible_v<R, std::in_place_type_t<T>>) {
+    if constexpr (is_consteval([] { return R{std::in_place_type<T>}; })) {
+      return true;
+    }
   }
   return false;
 }
@@ -407,32 +585,49 @@ struct dispatch_match_helper {
   using traits =
       conditional_traits<std::is_same_v<typename C::dispatch_type, D>>;
 };
+template <class D, class C>
+struct dispatch_match_traits : inapplicable_traits {};
+template <class D, class C>
+    requires(std::is_same_v<typename C::dispatch_type, D>)
+struct dispatch_match_traits<D, C> : applicable_traits {};
 template <class F, class... Cs>
 struct facade_conv_traits_impl : inapplicable_traits {};
-template <class F, class... Cs> requires(conv_traits<Cs>::applicable && ...)
+template <class F, class... Cs> requires((conv_traits<Cs>::applicable && ...) &&
+    (is_a11y_well_formed<typename Cs::dispatch_type, F>() && ...))
 struct facade_conv_traits_impl<F, Cs...> : applicable_traits {
   using conv_meta = composite_meta<typename conv_traits<Cs>::meta...>;
-  using conv_accessor = composite_accessor<
-      F, typename conv_traits<Cs>::dispatch_type...>;
+  using direct_conv_accessor =
+      composite_accessor<F, true, typename conv_traits<Cs>::dispatch_type...>;
+  using indirect_conv_accessor =
+      composite_accessor<F, false, typename conv_traits<Cs>::dispatch_type...>;
   template <class D, class... Args>
-  using matched_overload = typename conv_traits<
-      first_applicable_t<dispatch_match_helper<D>::template traits, Cs...>>
-      ::template matched_overload<Args...>;
+  using matched_overload_traits = typename conv_traits<first_applicable_t<
+      dispatch_match_helper<D>::template traits, Cs...>>
+      ::template matched_overload_traits<Args...>;
 
   template <class P>
   static constexpr bool conv_applicable_ptr =
       (conv_traits<Cs>::template applicable_ptr<P> && ...);
+  static constexpr bool conv_has_indirection =
+      (!Cs::dispatch_type::is_direct || ...);
 };
 template <class F, class... Rs>
-struct facade_refl_traits_impl {
-  using refl_meta = composite_meta<Rs...>;
-  using refl_accessor = composite_accessor<F, Rs...>;
+struct facade_refl_traits_impl : inapplicable_traits {};
+template <class F, class... Rs>
+    requires((is_meta_is_direct_well_formed<Rs>() && ...) &&
+        (is_a11y_well_formed<Rs, F>() && ...))
+struct facade_refl_traits_impl<F, Rs...> : applicable_traits {
+  using refl_meta = composite_meta<std::conditional_t<
+      Rs::is_direct, Rs, indirect_refl_meta<Rs>>...>;
+  using direct_refl_accessor = composite_accessor<F, true, Rs...>;
+  using indirect_refl_accessor = composite_accessor<F, false, Rs...>;
 
   template <class R>
   static constexpr bool has_refl = (std::is_same_v<R, Rs> || ...);
   template <class P>
   static constexpr bool refl_applicable_ptr =
       (is_reflection_type_well_formed<Rs, P>() && ...);
+  static constexpr bool refl_has_indirection = (!Rs::is_direct || ...);
 };
 template <class F> struct facade_traits : inapplicable_traits {};
 template <class F>
@@ -440,11 +635,14 @@ template <class F>
         requires {
           typename F::convention_types;
           typename F::reflection_types;
-        } && is_tuple_like_well_formed<typename F::convention_types>() &&
-        is_tuple_like_well_formed<typename F::reflection_types>() &&
+        } &&
         is_facade_constraints_well_formed<F>() &&
-        instantiated_t<facade_conv_traits_impl,
-            typename F::convention_types, F>::applicable)
+        is_tuple_like_well_formed<typename F::convention_types>() &&
+        instantiated_t<facade_conv_traits_impl, typename F::convention_types, F>
+            ::applicable &&
+        is_tuple_like_well_formed<typename F::reflection_types>() &&
+        instantiated_t<facade_refl_traits_impl, typename F::reflection_types, F>
+            ::applicable)
 struct facade_traits<F>
     : instantiated_t<facade_conv_traits_impl, typename F::convention_types, F>,
       instantiated_t<facade_refl_traits_impl, typename F::reflection_types, F> {
@@ -457,18 +655,16 @@ struct facade_traits<F>
   using meta = composite_meta<copyability_meta, relocatability_meta,
       destructibility_meta, typename facade_traits::conv_meta,
       typename facade_traits::refl_meta>;
-  using base = composite_accessor<F, typename facade_traits::conv_accessor,
-      typename facade_traits::refl_accessor>;
-
-  template <class P>
-  static constexpr bool applicable_ptr =
-      sizeof(P) <= F::constraints.max_size &&
-      alignof(P) <= F::constraints.max_align &&
-      has_copyability<P>(F::constraints.copyability) &&
-      has_relocatability<P>(F::constraints.relocatability) &&
-      has_destructibility<P>(F::constraints.destructibility) &&
-      facade_traits::template conv_applicable_ptr<P> &&
-      facade_traits::template refl_applicable_ptr<P>;
+  using direct_accessor = merged_composite_accessor<
+      typename facade_traits::direct_conv_accessor,
+      typename facade_traits::direct_refl_accessor>;
+  using indirect_accessor = merged_composite_accessor<
+      typename facade_traits::indirect_conv_accessor,
+      typename facade_traits::indirect_refl_accessor>;
+
+  static constexpr bool applicable = true;
+  static constexpr bool has_indirection = facade_traits::conv_has_indirection ||
+      facade_traits::refl_has_indirection;
 };
 
 using ptr_prototype = void*[2];
@@ -497,12 +693,28 @@ struct meta_ptr<M> : M {
 
 template <class F>
 struct proxy_helper {
+  template <class D, class P, class... Args>
+  using matched_overload_traits = typename facade_traits<F>
+      ::template matched_overload_traits<D, add_qualifier_t<
+          std::byte, qualifier_of_v<P&&>>, Args...>;
+
   static inline const auto& get_meta(const proxy<F>& p) noexcept
       { return *p.meta_.operator->(); }
+  template <class D, class P, class... Args>
+  static decltype(auto) invoke(P&& p, Args&&... args)
+      noexcept(matched_overload_traits<D, P, Args...>::is_noexcept)
+      requires(requires { typename matched_overload_traits<D, P, Args...>; }) {
+    return p.meta_->template dispatcher_meta<typename matched_overload_traits<
+        D, P, Args...>::template meta_provider<D>>::dispatcher(
+        std::forward<add_qualifier_t<std::byte, qualifier_of_v<P&&>>>(*p.ptr_),
+        std::forward<Args>(args)...);
+  }
+  static inline const proxy<F>& access(
+      const typename facade_traits<F>::indirect_accessor& ia) {
+    return *reinterpret_cast<const proxy<F>*>(
+        reinterpret_cast<const std::byte*>(&ia) - offsetof(proxy<F>, ia_));
+  }
 };
-template <class F, class D, class... Args>
-using proxy_overload = typename facade_traits<
-    lazy_eval_t<F, D, Args...>>::template matched_overload<D, Args...>;
 
 }  // namespace details
 
@@ -510,11 +722,18 @@ template <class F>
 concept facade = details::facade_traits<F>::applicable;
 
 template <class P, class F>
-concept proxiable = facade<F> && details::ptr_traits<P>::applicable &&
-    details::facade_traits<F>::template applicable_ptr<P>;
+concept proxiable = facade<F> && sizeof(P) <= F::constraints.max_size &&
+    alignof(P) <= F::constraints.max_align &&
+    details::has_copyability<P>(F::constraints.copyability) &&
+    details::has_relocatability<P>(F::constraints.relocatability) &&
+    details::has_destructibility<P>(F::constraints.destructibility) &&
+    (!details::facade_traits<F>::has_indirection ||
+        details::ptr_traits<P>::applicable) &&
+    details::facade_traits<F>::template conv_applicable_ptr<P> &&
+    details::facade_traits<F>::template refl_applicable_ptr<P>;
 
 template <class F>
-class proxy : public details::facade_traits<F>::base {
+class proxy : public details::facade_traits<F>::direct_accessor {
   friend struct details::proxy_helper<F>;
   using Traits = details::facade_traits<F>;
   static_assert(Traits::applicable);
@@ -561,52 +780,14 @@ class proxy : public details::facade_traits<F>::base {
   static constexpr bool HasMoveAssignment = HasMoveConstructor && HasDestructor;
 
  public:
-  template <class O>
-  class dispatch_ptr {
-    using OverloadTraits = details::overload_traits<O>;
-    using DispatcherType = typename OverloadTraits::dispatcher_type;
-
-   public:
-    constexpr dispatch_ptr() noexcept = default;
-    constexpr dispatch_ptr(const dispatch_ptr&) noexcept = default;
-    dispatch_ptr& operator=(const dispatch_ptr&) noexcept = default;
-
-#if defined(_MSC_VER) && !defined(__clang__)
-    template <class D>
-    constexpr explicit dispatch_ptr(std::in_place_type_t<D>) noexcept
-        : offset_(offsetof(Meta, template dispatcher_meta<typename
-              OverloadTraits::template meta_provider<D>>::dispatcher)) {}
-    DispatcherType get_dispatcher(const Meta& meta) const noexcept {
-      return *reinterpret_cast<const DispatcherType*>(
-          reinterpret_cast<const std::byte*>(&meta) + offset_);
-    }
+  using facade_type = F;
 
-   private:
-    std::size_t offset_;
-#else
-    template <class D>
-    constexpr explicit dispatch_ptr(std::in_place_type_t<D>) noexcept
-        : ptr_(&details::dispatcher_meta<typename OverloadTraits
-              ::template meta_provider<D>>::dispatcher) {}
-    DispatcherType get_dispatcher(const Meta& meta) const noexcept
-        { return meta.*ptr_; }
-
-   private:
-    DispatcherType Meta::* ptr_;
-#endif  // defined(_MSC_VER) && !defined(__clang__)
-  };
-  template <class D, class... Args>
-  static auto consteval get_dispatch_ptr()
-      requires(requires { typename details::proxy_overload<F, D, Args...>; }) {
-    return dispatch_ptr<details::proxy_overload<F, D, Args...>>{
-        std::in_place_type<D>};
-  }
   proxy() noexcept = default;
   proxy(std::nullptr_t) noexcept : proxy() {}
   proxy(const proxy& rhs) noexcept(HasNothrowCopyConstructor)
       requires(!HasTrivialCopyConstructor && HasCopyConstructor) {
     if (rhs.meta_.has_value()) {
-      rhs.meta_->Traits::copyability_meta::dispatcher(ptr_, rhs.ptr_);
+      rhs.meta_->Traits::copyability_meta::dispatcher(*ptr_, *rhs.ptr_);
       meta_ = rhs.meta_;
     } else {
       meta_.reset();
@@ -621,7 +802,7 @@ class proxy : public details::facade_traits<F>::base {
           constraint_level::trivial) {
         std::ranges::uninitialized_copy(rhs.ptr_, ptr_);
       } else {
-        rhs.meta_->Traits::relocatability_meta::dispatcher(ptr_, rhs.ptr_);
+        rhs.meta_->Traits::relocatability_meta::dispatcher(*ptr_, *rhs.ptr_);
       }
       meta_ = rhs.meta_;
       rhs.meta_.reset();
@@ -693,7 +874,7 @@ class proxy : public details::facade_traits<F>::base {
   ~proxy() noexcept(HasNothrowDestructor)
       requires(!HasTrivialDestructor && HasDestructor) {
     if (meta_.has_value()) {
-      meta_->Traits::destructibility_meta::dispatcher(ptr_);
+      meta_->Traits::destructibility_meta::dispatcher(*ptr_);
     }
   }
   ~proxy() requires(HasTrivialDestructor) = default;
@@ -736,16 +917,10 @@ class proxy : public details::facade_traits<F>::base {
     reset();
     return initialize<P>(il, std::forward<Args>(args)...);
   }
-
-  template <class O>
-  friend auto operator->*(const proxy& p, dispatch_ptr<O> ptd) noexcept {
-    return [&p, ptd]<class... Args>(Args&&... args)
-        noexcept(details::overload_traits<O>::is_noexcept) -> decltype(auto)
-        requires(details::overload_traits<O>::template matches<Args...>) {
-      return ptd.get_dispatcher(*p.meta_.operator->())(
-          p.ptr_, std::forward<Args>(args)...);
-    };
-  }
+  auto operator->() const noexcept requires(Traits::has_indirection)
+      { return &ia_; }
+  auto& operator*() const noexcept requires(Traits::has_indirection)
+      { return ia_; }
 
  private:
   template <class P, class... Args>
@@ -755,24 +930,48 @@ class proxy : public details::facade_traits<F>::base {
     return *std::launder(reinterpret_cast<P*>(ptr_));
   }
 
+  [[___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE]]
+  mutable typename Traits::indirect_accessor ia_;
   details::meta_ptr<Meta> meta_;
   alignas(F::constraints.max_align) std::byte ptr_[F::constraints.max_size];
 };
 
-template <class D, class F, class... Args>
+template <class D, facade F, class... Args>
+decltype(auto) proxy_invoke(proxy<F>& p, Args&&... args)
+    ___PRO_DIRECT_FUNC_IMPL(details::proxy_helper<F>::template invoke<D>(
+        p, std::forward<Args>(args)...))
+template <class D, facade F, class... Args>
 decltype(auto) proxy_invoke(const proxy<F>& p, Args&&... args)
-    noexcept(details::overload_traits<details::proxy_overload<F, D, Args...>>
-        ::is_noexcept)
-    requires(requires { typename details::proxy_overload<F, D, Args...>; }) {
-  constexpr auto ptd = proxy<F>::template get_dispatch_ptr<D, Args...>();
-  return (p->*ptd)(std::forward<Args>(args)...);
-}
-
-template <class R, class F>
+    ___PRO_DIRECT_FUNC_IMPL(details::proxy_helper<F>::template invoke<D>(
+        p, std::forward<Args>(args)...))
+template <class D, facade F, class... Args>
+decltype(auto) proxy_invoke(proxy<F>&& p, Args&&... args)
+    ___PRO_DIRECT_FUNC_IMPL(details::proxy_helper<F>::template invoke<D>(
+        static_cast<proxy<F>&&>(p), std::forward<Args>(args)...))
+template <class D, facade F, class... Args>
+decltype(auto) proxy_invoke(const proxy<F>&& p, Args&&... args)
+    ___PRO_DIRECT_FUNC_IMPL(details::proxy_helper<F>::template invoke<D>(
+        static_cast<const proxy<F>&&>(p), std::forward<Args>(args)...))
+
+template <class R, facade F>
 const R& proxy_reflect(const proxy<F>& p) noexcept
     requires(details::facade_traits<F>::template has_refl<R>)
     { return details::proxy_helper<F>::get_meta(p); }
 
+template <class P, class A>
+decltype(auto) access_proxy(A&& a) noexcept {
+  if constexpr (std::is_base_of_v<std::remove_cvref_t<A>, P>) {
+    return static_cast<details::add_qualifier_t<
+        P, details::qualifier_of_v<A&&>>>(std::forward<A>(a));
+  } else {
+    using IA = typename details::facade_traits<typename P::facade_type>
+        ::indirect_accessor;
+    static_assert(std::is_base_of_v<std::remove_cvref_t<A>, IA>);
+    return details::proxy_helper<typename P::facade_type>
+        ::access(static_cast<const IA&>(a));
+  }
+}
+
 namespace details {
 
 template <class T>
@@ -833,11 +1032,7 @@ class allocated_ptr {
   T* operator->() const noexcept { return ptr_; }
 
  private:
-#if __has_cpp_attribute(msvc::no_unique_address)
-  [[msvc::no_unique_address]]
-#elif __has_cpp_attribute(no_unique_address)
-  [[__no_unique_address__]]
-#endif
+  [[___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE]]
   Alloc alloc_;
   T* ptr_;
 };
@@ -1052,11 +1247,10 @@ using merge_conv_tuple_t = instantiated_t<merge_conv_tuple_impl_t, Cs1, Cs0>;
 template <class Cs, class Rs, proxiable_ptr_constraints C>
 struct facade_builder_impl {
   template <class D, class... Os>
-      requires(std::is_nothrow_default_constructible_v<D> &&
-          (overload_traits<Os>::applicable && ...))
+      requires(conv_traits<conv_impl<D, std::tuple<Os...>>>::applicable)
   using add_convention = facade_builder_impl<add_conv_t<
       Cs, conv_impl<D, std::tuple<Os...>>>, Rs, C>;
-  template <class R>
+  template <class R> requires(is_meta_is_direct_well_formed<R>())
   using add_reflection = facade_builder_impl<Cs, add_tuple_t<Rs, R>, C>;
   template <facade F>
   using add_facade = facade_builder_impl<
@@ -1089,77 +1283,57 @@ using facade_builder = details::facade_builder_impl<std::tuple<>, std::tuple<>,
         .relocatability = details::invalid_cl,
         .destructibility = details::invalid_cl}>;
 
-#define ___PRO_EXPAND_IMPL(__X) __X
-#define ___PRO_EXPAND_MACRO_IMPL(__MACRO, __1, __2, __3, __4, __NAME, ...) \
-    __MACRO ## _ ## __NAME
-#define ___PRO_EXPAND_MACRO(__MACRO, ...) \
-    ___PRO_EXPAND_IMPL(___PRO_EXPAND_MACRO_IMPL( \
-        __MACRO, __VA_ARGS__, 4, 3, 2)(__VA_ARGS__))
-
-#define ___PRO_DIRECT_FUNC_IMPL(...) \
-    noexcept(noexcept(__VA_ARGS__)) requires(requires { __VA_ARGS__; }) \
-    { return __VA_ARGS__; }
-
-#define ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC) \
+#define ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, __QP, __SELF, __ARGS) \
+    noexcept(noexcept(::pro::proxy_invoke<__D>( \
+        ::std::declval<__QP>(), __ARGS))) \
+    requires(requires { ::pro::proxy_invoke<__D>( \
+        ::std::declval<__QP>(), __ARGS); }) \
+    { return ::pro::proxy_invoke<__D>( \
+        ::pro::access_proxy<__P>(__SELF), __ARGS); }
+
+#define ___PRO_GEN_MEM_ACCESSOR_0(__SELF, __DECL, __D, __P, __ARGS, ...) \
+    __DECL(, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, const __P&, *this, __ARGS)
+#define ___PRO_GEN_MEM_ACCESSOR_1(__SELF, __DECL, __D, __P, __ARGS, ...) \
+    __DECL(&, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, __P&, *this, __ARGS) \
+    __DECL(const&, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, const __P&, *this, __ARGS) \
+    __DECL(&&, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, __P&&, \
+            ::std::forward<__SELF>(*this), __ARGS) \
+    __DECL(const&&, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, const __P&&, \
+            ::std::forward<const __SELF>(*this), __ARGS) \
+
+#define ___PRO_GEN_FREE_ACCESSOR_0(__SELF, __DECL, __D, __P, __ARGS, ...) \
+    __DECL(__SELF& __self, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, const __P&, __self, __ARGS)
+#define ___PRO_GEN_FREE_ACCESSOR_1(__SELF, __DECL, __D, __P, __ARGS, ...) \
+    __DECL(__SELF& __self, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, __P&, __self, __ARGS) \
+    __DECL(const __SELF& __self, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, const __P&, __self, __ARGS) \
+    __DECL(__SELF&& __self, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, __P&&, \
+            ::std::forward<__SELF>(__self), __ARGS) \
+    __DECL(const __SELF&& __self, __VA_ARGS__) \
+        ___PRO_ACCESSOR_FUNC_IMPL(__D, __P, const __P&&, \
+            ::std::forward<const __SELF>(__self), __ARGS) \
+
+#define ___PRO_DECL_MEM_ACCESSOR(__Q, __FNAME) \
+    template <class... __Args> decltype(auto) __FNAME(__Args&&... __args) __Q
+
+#define ___PRO_DECL_FREE_ACCESSOR(__SELF, __FNAME) \
     template <class... __Args> \
-    decltype(auto) operator()(::std::nullptr_t, __Args&&... __args) \
-        ___PRO_DIRECT_FUNC_IMPL(__DEFFUNC(::std::forward<__Args>(__args)...))
-
-#define ___PRO_DEF_MEM_DISPATCH_IMPL(__NAME, __FUNC, __FNAME, ...) \
-    struct __NAME { \
-      using __name = __NAME; \
-      template <class... __Args> \
-      decltype(auto) operator()(auto& __self, __Args&&... __args) \
-          ___PRO_DIRECT_FUNC_IMPL( \
-              __self.__FUNC(::std::forward<__Args>(__args)...)) \
-      template <class __P> \
-      struct accessor { \
-        template <class... __Args> \
-        decltype(auto) __FNAME(__Args&&... __args) const \
-            ___PRO_DIRECT_FUNC_IMPL(::pro::proxy_invoke<__name>( \
-                static_cast<::pro::details::lazy_eval_t<const __P&, \
-                    __Args...>>(*this), ::std::forward<__Args>(__args)...)) \
-      }; \
-      __VA_ARGS__ \
-    }
-#define ___PRO_DEF_MEM_DISPATCH_2(__NAME, __FUNC) \
-    ___PRO_DEF_MEM_DISPATCH_IMPL(__NAME, __FUNC, __FUNC)
-#define ___PRO_DEF_MEM_DISPATCH_3(__NAME, __FUNC, __FNAME) \
-    ___PRO_DEF_MEM_DISPATCH_IMPL(__NAME, __FUNC, __FNAME)
-#define ___PRO_DEF_MEM_DISPATCH_4(__NAME, __FUNC, __FNAME, __DEFFUNC) \
-    ___PRO_DEF_MEM_DISPATCH_IMPL( \
-        __NAME, __FUNC, __FNAME, ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC))
-#define PRO_DEF_MEM_DISPATCH(__NAME, ...) \
-    ___PRO_EXPAND_MACRO(___PRO_DEF_MEM_DISPATCH, __NAME, __VA_ARGS__)
-
-#define ___PRO_DEF_FREE_DISPATCH_IMPL(__NAME, __FUNC, __FNAME, ...) \
-    struct __NAME { \
-      using __name = __NAME; \
-      template <class... __Args> \
-      decltype(auto) operator()(auto& __self, __Args&&... __args) \
-          ___PRO_DIRECT_FUNC_IMPL( \
-              __FUNC(__self, ::std::forward<__Args>(__args)...)) \
-      template <class __P> \
-      struct accessor { \
-        template <class... __Args> \
-        friend decltype(auto) __FNAME(const __P& __self, __Args&&... __args) \
-            ___PRO_DIRECT_FUNC_IMPL(::pro::proxy_invoke<__name>( \
-                __self, ::std::forward<__Args>(__args)...)) \
-      }; \
-      __VA_ARGS__ \
-    }
-#define ___PRO_DEF_FREE_DISPATCH_2(__NAME, __FUNC) \
-    ___PRO_DEF_FREE_DISPATCH_IMPL(__NAME, __FUNC, __FUNC)
-#define ___PRO_DEF_FREE_DISPATCH_3(__NAME, __FUNC, __FNAME) \
-    ___PRO_DEF_FREE_DISPATCH_IMPL(__NAME, __FUNC, __FNAME)
-#define ___PRO_DEF_FREE_DISPATCH_4(__NAME, __FUNC, __FNAME, __DEFFUNC) \
-    ___PRO_DEF_FREE_DISPATCH_IMPL( \
-        __NAME, __FUNC, __FNAME, ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC))
-#define PRO_DEF_FREE_DISPATCH(__NAME, ...) \
-    ___PRO_EXPAND_MACRO(___PRO_DEF_FREE_DISPATCH, __NAME, __VA_ARGS__)
+    friend decltype(auto) __FNAME(__SELF, __Args&&... __args)
 
 namespace details {
 
+template <int IS_DIRECT>
+struct dispatch_base
+    { static constexpr bool is_direct = static_cast<bool>(IS_DIRECT); };
+
 template <std::size_t N>
 struct sign {
   consteval sign(const char (&str)[N])
@@ -1180,33 +1354,75 @@ struct op_dispatch_traits_impl : inapplicable_traits {};
     struct op_dispatch_traits_impl<#__VA_ARGS__, sign_pos_type::left> \
         : applicable_traits { \
       struct base { \
-        decltype(auto) operator()(auto& self) \
-            ___PRO_DIRECT_FUNC_IMPL(__VA_ARGS__ self) \
+        template <class T> \
+        decltype(auto) operator()(T&& self) \
+            ___PRO_DIRECT_FUNC_IMPL(__VA_ARGS__ std::forward<T>(self)) \
       }; \
       template <class D, class P> \
-      struct accessor { \
+      struct indirect_accessor { \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ () \
+            noexcept(noexcept( \
+                proxy_invoke<lazy_eval_t<D>>(std::declval<const P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+      }; \
+      template <class D, class P> \
+      struct direct_accessor { \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ () & \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ () const& \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ () && \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<P&&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<P&&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>( \
+                std::forward<direct_accessor>(*this))); } \
         template <class Barrier = void> \
-        decltype(auto) operator __VA_ARGS__ () const \
-            ___PRO_DIRECT_FUNC_IMPL(proxy_invoke<D>( \
-                static_cast<lazy_eval_t<const P&, Barrier>>(*this))) \
+        decltype(auto) operator __VA_ARGS__ () const&& \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>( \
+                std::forward<const direct_accessor>(*this))); } \
       }; \
     };
 
+#define ___PRO_DECL_OPERATOR_ACCESSOR_PREFIX_1(SELF, ...) \
+    template <class Arg> \
+    friend decltype(auto) operator __VA_ARGS__ (Arg&& arg, SELF)
 #define ___PRO_OPERATOR_DISPATCH_TRAITS_PREFIX_1_IMPL(...) \
     template <> \
     struct op_dispatch_traits_impl<#__VA_ARGS__, sign_pos_type::left> \
         : applicable_traits { \
       struct base { \
-        template <class Arg> \
-        decltype(auto) operator()(auto& self, Arg&& arg) \
-            ___PRO_DIRECT_FUNC_IMPL(std::forward<Arg>(arg) __VA_ARGS__ self) \
+        template <class T, class Arg> \
+        decltype(auto) operator()(T&& self, Arg&& arg) \
+            ___PRO_DIRECT_FUNC_IMPL(std::forward<Arg>(arg) __VA_ARGS__ \
+                std::forward<T>(self)) \
       }; \
       template <class D, class P> \
-      struct accessor { \
-        template <class Arg> \
-        friend decltype(auto) operator __VA_ARGS__ (Arg&& arg, const P& self) \
-            ___PRO_DIRECT_FUNC_IMPL( \
-                proxy_invoke<D>(self, std::forward<Arg>(arg))) \
+      struct indirect_accessor { \
+        ___PRO_GEN_FREE_ACCESSOR_0( \
+            indirect_accessor, ___PRO_DECL_OPERATOR_ACCESSOR_PREFIX_1, D, P, \
+                std::forward<Arg>(arg), __VA_ARGS__) \
+      }; \
+      template <class D, class P> \
+      struct direct_accessor { \
+        ___PRO_GEN_FREE_ACCESSOR_1( \
+            direct_accessor, ___PRO_DECL_OPERATOR_ACCESSOR_PREFIX_1, D, P, \
+                std::forward<Arg>(arg), __VA_ARGS__) \
       }; \
     };
 
@@ -1215,22 +1431,57 @@ struct op_dispatch_traits_impl : inapplicable_traits {};
     struct op_dispatch_traits_impl<#__VA_ARGS__, sign_pos_type::left> \
         : applicable_traits { \
       struct base { \
-        decltype(auto) operator()(auto& self) \
-            ___PRO_DIRECT_FUNC_IMPL(__VA_ARGS__ self) \
-        template <class Arg> \
-        decltype(auto) operator()(auto& self, Arg&& arg) \
-            ___PRO_DIRECT_FUNC_IMPL(std::forward<Arg>(arg) __VA_ARGS__ self) \
+        template <class T> \
+        decltype(auto) operator()(T&& self) \
+            ___PRO_DIRECT_FUNC_IMPL(__VA_ARGS__ std::forward<T>(self)) \
+        template <class T, class Arg> \
+        decltype(auto) operator()(T&& self, Arg&& arg) \
+            ___PRO_DIRECT_FUNC_IMPL(std::forward<Arg>(arg) __VA_ARGS__ \
+                std::forward<T>(self)) \
       }; \
       template <class D, class P> \
-      struct accessor { \
+      struct indirect_accessor { \
         template <class Barrier = void> \
-        decltype(auto) operator __VA_ARGS__ () const \
-            ___PRO_DIRECT_FUNC_IMPL(proxy_invoke<D>( \
-                static_cast<lazy_eval_t<const P&, Barrier>>(*this))) \
-        template <class Arg> \
-        friend decltype(auto) operator __VA_ARGS__ (Arg&& arg, const P& self) \
-            ___PRO_DIRECT_FUNC_IMPL( \
-                proxy_invoke<D>(self, std::forward<Arg>(arg))) \
+        decltype(auto) operator __VA_ARGS__ () \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+        ___PRO_GEN_FREE_ACCESSOR_0( \
+            indirect_accessor, ___PRO_DECL_OPERATOR_ACCESSOR_PREFIX_1, D, P, \
+                std::forward<Arg>(arg), __VA_ARGS__) \
+      }; \
+      template <class D, class P> \
+      struct direct_accessor { \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ () & \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ () const& \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ () && \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<P&&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<P&&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>( \
+                std::forward<direct_accessor>(*this))); } \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ () const&& \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>( \
+                std::forward<const direct_accessor>(*this))); } \
+        ___PRO_GEN_FREE_ACCESSOR_1( \
+            direct_accessor, ___PRO_DECL_OPERATOR_ACCESSOR_PREFIX_1, D, P, \
+                std::forward<Arg>(arg), __VA_ARGS__) \
       }; \
     };
 
@@ -1239,33 +1490,74 @@ struct op_dispatch_traits_impl : inapplicable_traits {};
     struct op_dispatch_traits_impl<#__VA_ARGS__, sign_pos_type::right> \
         : applicable_traits { \
       struct base { \
-        decltype(auto) operator()(auto& self) \
-            ___PRO_DIRECT_FUNC_IMPL(self __VA_ARGS__) \
+        template <class T> \
+        decltype(auto) operator()(T&& self) \
+            ___PRO_DIRECT_FUNC_IMPL(std::forward<T>(self) __VA_ARGS__) \
       }; \
       template <class D, class P> \
-      struct accessor { \
+      struct indirect_accessor { \
         template <class Barrier = void> \
-        decltype(auto) operator __VA_ARGS__ (int) const \
-            ___PRO_DIRECT_FUNC_IMPL(proxy_invoke<D>( \
-                static_cast<lazy_eval_t<const P&, Barrier>>(*this))) \
+        decltype(auto) operator __VA_ARGS__ (int) \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+      }; \
+      template <class D, class P> \
+      struct direct_accessor { \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ (int) & \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ (int) const& \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>(*this)); } \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ (int) && \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<P&&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<P&&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>( \
+                std::forward<direct_accessor>(*this))); } \
+        template <class Barrier = void> \
+        decltype(auto) operator __VA_ARGS__ (int) const&& \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&&>()))) \
+            requires(requires { proxy_invoke< \
+                lazy_eval_t<D, Barrier>>(std::declval<const P&&>()); }) \
+            { return proxy_invoke<D>(access_proxy<P>( \
+                std::forward<const direct_accessor>(*this))); } \
       }; \
     };
 
+#define ___PRO_DECL_OPERATOR_ACCESSOR_POSTFIX_1(Q, ...) \
+    template <class Arg> \
+    decltype(auto) operator __VA_ARGS__ (Arg&& arg) Q
 #define ___PRO_OPERATOR_DISPATCH_TRAITS_POSTFIX_1_IMPL(...) \
     template <> \
     struct op_dispatch_traits_impl<#__VA_ARGS__, sign_pos_type::right> \
         : applicable_traits { \
       struct base { \
-        template <class Arg> \
-        decltype(auto) operator()(auto& self, Arg&& arg) \
-            ___PRO_DIRECT_FUNC_IMPL(self __VA_ARGS__ std::forward<Arg>(arg)) \
+        template <class T, class Arg> \
+        decltype(auto) operator()(T&& self, Arg&& arg) \
+            ___PRO_DIRECT_FUNC_IMPL(std::forward<T>(self) __VA_ARGS__ \
+                std::forward<Arg>(arg)) \
       }; \
       template <class D, class P> \
-      struct accessor { \
-        template <class Arg> \
-        decltype(auto) operator __VA_ARGS__ (Arg&& arg) const \
-            ___PRO_DIRECT_FUNC_IMPL(proxy_invoke<D>(static_cast< \
-                lazy_eval_t<const P&, Arg>>(*this), std::forward<Arg>(arg))) \
+      struct indirect_accessor { \
+        ___PRO_GEN_MEM_ACCESSOR_0( \
+            indirect_accessor, ___PRO_DECL_OPERATOR_ACCESSOR_POSTFIX_1, D, P, \
+                std::forward<Arg>(arg), __VA_ARGS__) \
+      }; \
+      template <class D, class P> \
+      struct direct_accessor { \
+        ___PRO_GEN_MEM_ACCESSOR_1( \
+            direct_accessor, ___PRO_DECL_OPERATOR_ACCESSOR_POSTFIX_1, D, P, \
+                std::forward<Arg>(arg), __VA_ARGS__) \
       }; \
     };
 
@@ -1279,12 +1571,28 @@ struct op_dispatch_traits_impl : inapplicable_traits {};
             ___PRO_DIRECT_FUNC_IMPL(self __VA_ARGS__ std::forward<Arg>(arg)) \
       }; \
       template <class D, class P> \
-      struct accessor { \
+      struct indirect_accessor { \
         template <class Arg> \
-        const P& operator __VA_ARGS__ (Arg&& arg) const \
-            ___PRO_DIRECT_FUNC_IMPL(proxy_invoke<D>(static_cast< \
-                lazy_eval_t<const P&, Arg>>(*this), std::forward<Arg>(arg)), \
-                static_cast<lazy_eval_t<const P&, Arg>>(*this)) \
+        decltype(auto) operator __VA_ARGS__ (Arg&& arg) \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<const P&>(), \
+                std::forward<Arg>(arg)))) \
+            requires(requires { proxy_invoke<D>(std::declval<const P&>(), \
+                std::forward<Arg>(arg)); }) { \
+          proxy_invoke<D>(access_proxy<P>(*this), std::forward<Arg>(arg)); \
+          return *access_proxy<P>(*this); \
+        } \
+      }; \
+      template <class D, class P> \
+      struct direct_accessor { \
+        template <class Arg> \
+        decltype(auto) operator __VA_ARGS__ (Arg&& arg) \
+            noexcept(noexcept(proxy_invoke<D>(std::declval<P&>(), \
+                std::forward<Arg>(arg)))) \
+            requires(requires { proxy_invoke<D>(std::declval<P&>(), \
+                std::forward<Arg>(arg)); }) { \
+          proxy_invoke<D>(access_proxy<P>(*this), std::forward<Arg>(arg)); \
+          return access_proxy<P>(*this); \
+        } \
       }; \
     };
 
@@ -1337,38 +1645,33 @@ ___PRO_OPERATOR_DISPATCH_TRAITS_BINARY_IMPL(->*)
 #undef ___PRO_OPERATOR_DISPATCH_TRAITS_BINARY_IMPL
 #undef ___PRO_OPERATOR_DISPATCH_TRAITS_ASSIGNMENT_IMPL
 #undef ___PRO_OPERATOR_DISPATCH_TRAITS_POSTFIX_1_IMPL
+#undef ___PRO_DECL_OPERATOR_ACCESSOR_POSTFIX_1
 #undef ___PRO_OPERATOR_DISPATCH_TRAITS_POSTFIX_0_IMPL
 #undef ___PRO_OPERATOR_DISPATCH_TRAITS_PREFIX_0_OR_1_IMPL
 #undef ___PRO_OPERATOR_DISPATCH_TRAITS_PREFIX_1_IMPL
+#undef ___PRO_DECL_OPERATOR_ACCESSOR_PREFIX_1
 #undef ___PRO_OPERATOR_DISPATCH_TRAITS_PREFIX_0_IMPL
 
-template <>
-struct op_dispatch_traits_impl<"->", sign_pos_type::right>
-    : applicable_traits {
-  struct base
-      { auto operator()(auto& self) noexcept { return std::addressof(self); } };
-  template <class D, class P>
-  struct accessor {
-    template <class Barrier = void>
-    decltype(auto) operator->() const
-        ___PRO_DIRECT_FUNC_IMPL(proxy_invoke<D>(static_cast<
-            lazy_eval_t<const P&, Barrier>>(*this)))
-  };
-};
 template <>
 struct op_dispatch_traits_impl<"()", sign_pos_type::right>
     : applicable_traits {
   struct base {
-    template <class... Args>
-    decltype(auto) operator()(auto& self, Args&&... args)
-        ___PRO_DIRECT_FUNC_IMPL(self(std::forward<Args>(args)...))
+    template <class T, class... Args>
+    decltype(auto) operator()(T&& self, Args&&... args)
+        ___PRO_DIRECT_FUNC_IMPL(
+            std::forward<T>(self)(std::forward<Args>(args)...))
   };
   template <class D, class P>
-  struct accessor {
-    template <class... Args>
-    decltype(auto) operator()(Args&&... args) const
-        ___PRO_DIRECT_FUNC_IMPL(proxy_invoke<D>(static_cast<lazy_eval_t<
-            const P&, Args...>>(*this), std::forward<Args>(args)...))
+  struct indirect_accessor {
+      ___PRO_GEN_MEM_ACCESSOR_0(
+          indirect_accessor, ___PRO_DECL_MEM_ACCESSOR, D, P,
+              std::forward<__Args>(__args)..., operator())
+  };
+  template <class D, class P>
+  struct direct_accessor {
+      ___PRO_GEN_MEM_ACCESSOR_1(
+          direct_accessor, ___PRO_DECL_MEM_ACCESSOR, D, P,
+              std::forward<__Args>(__args)..., operator())
   };
 };
 template <>
@@ -1376,21 +1679,27 @@ struct op_dispatch_traits_impl<"[]", sign_pos_type::right>
     : applicable_traits {
   struct base {
 #if defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202110L
-    template <class... Args>
-    decltype(auto) operator()(auto& self, Args&&... args)
-        ___PRO_DIRECT_FUNC_IMPL(self[std::forward<Args>(args)...])
+    template <class T, class... Args>
+    decltype(auto) operator()(T&& self, Args&&... args)
+        ___PRO_DIRECT_FUNC_IMPL(
+            std::forward<T>(self)[std::forward<Args>(args)...])
 #else
-    template <class Arg>
-    decltype(auto) operator()(auto& self, Arg&& arg)
-        ___PRO_DIRECT_FUNC_IMPL(self[std::forward<Arg>(arg)])
+    template <class T, class Arg>
+    decltype(auto) operator()(T&& self, Arg&& arg)
+        ___PRO_DIRECT_FUNC_IMPL(std::forward<T>(self)[std::forward<Arg>(arg)])
 #endif  // defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202110L
   };
   template <class D, class P>
-  struct accessor {
-    template <class... Args>
-    decltype(auto) operator[](Args&&... args) const
-        ___PRO_DIRECT_FUNC_IMPL(proxy_invoke<D>(static_cast<lazy_eval_t<
-            const P&, Args...>>(*this), std::forward<Args>(args)...))
+  struct indirect_accessor {
+      ___PRO_GEN_MEM_ACCESSOR_0(
+          indirect_accessor, ___PRO_DECL_MEM_ACCESSOR, D, P,
+              std::forward<__Args>(__args)..., operator[])
+  };
+  template <class D, class P>
+  struct direct_accessor {
+      ___PRO_GEN_MEM_ACCESSOR_1(
+          direct_accessor, ___PRO_DECL_MEM_ACCESSOR, D, P,
+              std::forward<__Args>(__args)..., operator[])
   };
 };
 
@@ -1407,59 +1716,230 @@ template <sign SIGN>
 struct op_dispatch_traits<SIGN, sign_pos_type::none>
     : op_dispatch_traits_impl<SIGN, sign_pos_type::left> {};
 
-template <sign SIGN, sign_pos_type POS>
-using op_dispatch_base = typename op_dispatch_traits<SIGN, POS>::base;
-template <sign SIGN, sign_pos_type POS, class D, class P>
-using op_dispatch_accessor = typename op_dispatch_traits<SIGN, POS>
-    ::template accessor<D, P>;
+template <int IS_DIRECT, sign SIGN, sign_pos_type POS>
+struct op_dispatch_base :
+    dispatch_base<IS_DIRECT>, op_dispatch_traits<SIGN, POS>::base {};
+template <int IS_DIRECT, sign SIGN, sign_pos_type POS, class D, class P>
+using op_dispatch_accessor = std::conditional_t<static_cast<bool>(IS_DIRECT),
+    typename op_dispatch_traits<SIGN, POS>::template direct_accessor<D, P>,
+    typename op_dispatch_traits<SIGN, POS>::template indirect_accessor<D, P>>;
+
+template <class T>
+struct conversion_dispatch_base_impl {
+  template <class U>
+  T operator()(U&& self)
+      ___PRO_DIRECT_FUNC_IMPL(static_cast<T>(std::forward<U>(self)))
+};
+template <int IS_DIRECT, class T>
+struct conversion_dispatch_base
+    : dispatch_base<IS_DIRECT>, conversion_dispatch_base_impl<T> {};
+template <class T, class D, class P>
+struct indirect_conversion_accessor {
+  template <class Barrier = void>
+  explicit operator T()
+      noexcept(noexcept(proxy_invoke<D>(std::declval<const P&>())))
+      requires(requires { proxy_invoke<
+          lazy_eval_t<D, Barrier>>(std::declval<const P&>()); })
+      { return proxy_invoke<D>(access_proxy<P>(*this)); }
+};
+template <class T, class D, class P>
+struct direct_conversion_accessor {
+  static_assert(std::is_nothrow_default_constructible_v<T>);
+  template <class Barrier = void>
+  explicit operator T() &
+      noexcept(noexcept(proxy_invoke<D>(std::declval<P&>())))
+      requires(requires { proxy_invoke<
+          lazy_eval_t<D, Barrier>>(std::declval<P&>()); }) {
+    if (access_proxy<P>(*this).has_value()) {
+      return proxy_invoke<D>(access_proxy<P>(*this));
+    } else {
+      return T{};
+    }
+  }
+  template <class Barrier = void>
+  explicit operator T() const&
+      noexcept(noexcept(proxy_invoke<D>(std::declval<const P&>())))
+      requires(requires { proxy_invoke<
+          lazy_eval_t<D, Barrier>>(std::declval<const P&>()); }) {
+    if (access_proxy<P>(*this).has_value()) {
+      return proxy_invoke<D>(access_proxy<P>(*this));
+    } else {
+      return T{};
+    }
+  }
+  template <class Barrier = void>
+  explicit operator T() &&
+      noexcept(noexcept(proxy_invoke<D>(std::declval<P&&>())))
+      requires(requires { proxy_invoke<
+          lazy_eval_t<D, Barrier>>(std::declval<P&&>()); }) {
+    if (access_proxy<P>(*this).has_value()) {
+      return proxy_invoke<D>(access_proxy<P>(std::forward<
+          direct_conversion_accessor>(*this)));
+    } else {
+      return T{};
+    }
+  }
+  template <class Barrier = void>
+  explicit operator T() const&&
+      noexcept(noexcept(proxy_invoke<D>(std::declval<const P&&>())))
+      requires(requires { proxy_invoke<
+          lazy_eval_t<D, Barrier>>(std::declval<const P&&>()); }) {
+    if (access_proxy<P>(*this).has_value()) {
+      return proxy_invoke<D>(access_proxy<P>(std::forward<
+          const direct_conversion_accessor>(*this)));
+    } else {
+      return T{};
+    }
+  }
+};
+template <int IS_DIRECT, class T, class D, class P>
+using conversion_dispatch_accessor = std::conditional_t<
+    static_cast<bool>(IS_DIRECT), direct_conversion_accessor<T, D, P>,
+    indirect_conversion_accessor<T, D, P>>;
 
 }  // namespace details
 
-#define ___PRO_DEF_OPERATOR_DISPATCH_IMPL(__NAME, __POS, __SIGN, ...) \
-    struct __NAME : ::pro::details::op_dispatch_base< \
-        __SIGN, ::pro::details::sign_pos_type::__POS> { \
-      using ::pro::details::op_dispatch_base< \
-          __SIGN, ::pro::details::sign_pos_type::__POS>::operator(); \
+#define ___PRO_EXPAND_IMPL(__X) __X
+#define ___PRO_EXPAND_MACRO_IMPL( \
+    __MACRO, __1, __2, __3, __4, __5, __NAME, ...) \
+    __MACRO##_##__NAME
+#define ___PRO_EXPAND_MACRO(__MACRO, ...) \
+    ___PRO_EXPAND_IMPL(___PRO_EXPAND_MACRO_IMPL( \
+        __MACRO, __VA_ARGS__, 5, 4, 3)(__VA_ARGS__))
+
+#define ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC) \
+    template <class... __Args> \
+    decltype(auto) operator()(::std::nullptr_t, __Args&&... __args) \
+        ___PRO_DIRECT_FUNC_IMPL(__DEFFUNC(::std::forward<__Args>(__args)...))
+
+#define ___PRO_DEF_MEM_DISPATCH_IMPL( \
+    __NAME, __IS_DIRECT, __FUNC, __FNAME, ...) \
+    struct __NAME : ::pro::details::dispatch_base<__IS_DIRECT> { \
+      using __name = __NAME; \
+      template <class __T, class... __Args> \
+      decltype(auto) operator()(__T&& __self, __Args&&... __args) \
+          ___PRO_DIRECT_FUNC_IMPL(::std::forward<__T>(__self) \
+              .__FUNC(::std::forward<__Args>(__args)...)) \
       template <class __P> \
-      using accessor = ::pro::details::op_dispatch_accessor< \
+      struct accessor { \
+        ___PRO_GEN_MEM_ACCESSOR_##__IS_DIRECT( \
+            accessor, ___PRO_DECL_MEM_ACCESSOR, __name, __P, \
+                ::std::forward<__Args>(__args)..., __FNAME) \
+      }; \
+      __VA_ARGS__ \
+    }
+#define ___PRO_DEF_MEM_DISPATCH_3(__NAME, __IS_DIRECT, __FUNC) \
+    ___PRO_DEF_MEM_DISPATCH_IMPL(__NAME, __IS_DIRECT, __FUNC, __FUNC)
+#define ___PRO_DEF_MEM_DISPATCH_4(__NAME, __IS_DIRECT, __FUNC, __FNAME) \
+    ___PRO_DEF_MEM_DISPATCH_IMPL(__NAME, __IS_DIRECT, __FUNC, __FNAME)
+#define ___PRO_DEF_MEM_DISPATCH_5( \
+    __NAME, __IS_DIRECT, __FUNC, __FNAME, __DEFFUNC) \
+    ___PRO_DEF_MEM_DISPATCH_IMPL(__NAME, __IS_DIRECT, __FUNC, __FNAME, \
+        ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC))
+#define PRO_DEF_INDIRECT_MEM_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_MEM_DISPATCH, __NAME, 0, __VA_ARGS__)
+#define PRO_DEF_DIRECT_MEM_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_MEM_DISPATCH, __NAME, 1, __VA_ARGS__)
+#define PRO_DEF_MEM_DISPATCH(__NAME, ...) \
+    PRO_DEF_INDIRECT_MEM_DISPATCH(__NAME, __VA_ARGS__)
+
+#define ___PRO_DEF_FREE_DISPATCH_IMPL( \
+    __NAME, __IS_DIRECT, __FUNC, __FNAME, ...) \
+    struct __NAME : ::pro::details::dispatch_base<__IS_DIRECT> { \
+      using __name = __NAME; \
+      template <class __T, class... __Args> \
+      decltype(auto) operator()(__T&& __self, __Args&&... __args) \
+          ___PRO_DIRECT_FUNC_IMPL(__FUNC(::std::forward<__T>(__self), \
+              ::std::forward<__Args>(__args)...)) \
+      template <class __P> \
+      struct accessor { \
+        ___PRO_GEN_FREE_ACCESSOR_##__IS_DIRECT( \
+            accessor, ___PRO_DECL_FREE_ACCESSOR, __name, __P, \
+                ::std::forward<__Args>(__args)..., __FNAME) \
+      }; \
+      __VA_ARGS__ \
+    }
+#define ___PRO_DEF_FREE_DISPATCH_3(__NAME, __IS_DIRECT, __FUNC) \
+    ___PRO_DEF_FREE_DISPATCH_IMPL(__NAME, __IS_DIRECT, __FUNC, __FUNC)
+#define ___PRO_DEF_FREE_DISPATCH_4(__NAME, __IS_DIRECT, __FUNC, __FNAME) \
+    ___PRO_DEF_FREE_DISPATCH_IMPL(__NAME, __IS_DIRECT, __FUNC, __FNAME)
+#define ___PRO_DEF_FREE_DISPATCH_5( \
+    __NAME, __IS_DIRECT, __FUNC, __FNAME, __DEFFUNC) \
+    ___PRO_DEF_FREE_DISPATCH_IMPL(__NAME, __IS_DIRECT, __FUNC, __FNAME, \
+        ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC))
+#define PRO_DEF_INDIRECT_FREE_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_FREE_DISPATCH, __NAME, 0, __VA_ARGS__)
+#define PRO_DEF_DIRECT_FREE_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_FREE_DISPATCH, __NAME, 1, __VA_ARGS__)
+#define PRO_DEF_FREE_DISPATCH(__NAME, ...) \
+    PRO_DEF_INDIRECT_FREE_DISPATCH(__NAME, __VA_ARGS__)
+
+#define ___PRO_DEF_OPERATOR_DISPATCH_IMPL( \
+    __NAME, __IS_DIRECT, __POS, __SIGN, ...) \
+    struct __NAME : ::pro::details::op_dispatch_base<__IS_DIRECT, __SIGN, \
+        ::pro::details::sign_pos_type::__POS> { \
+      using ::pro::details::op_dispatch_base<__IS_DIRECT, __SIGN, \
+          ::pro::details::sign_pos_type::__POS>::operator(); \
+      template <class __P> \
+      using accessor = ::pro::details::op_dispatch_accessor<__IS_DIRECT, \
           __SIGN, ::pro::details::sign_pos_type::__POS, __NAME, __P>; \
       __VA_ARGS__ \
     };
-#define ___PRO_DEF_OPERATOR_DISPATCH_3(__NAME, __POS, __SIGN) \
-    ___PRO_DEF_OPERATOR_DISPATCH_IMPL(__NAME, __POS, __SIGN)
-#define ___PRO_DEF_OPERATOR_DISPATCH_4(__NAME, __POS, __SIGN, __DEFFUNC) \
-    ___PRO_DEF_OPERATOR_DISPATCH_IMPL( \
-        __NAME, __POS, __SIGN, ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC))
+#define ___PRO_DEF_OPERATOR_DISPATCH_4(__NAME, __IS_DIRECT, __POS, __SIGN) \
+    ___PRO_DEF_OPERATOR_DISPATCH_IMPL(__NAME, __IS_DIRECT, __POS, __SIGN)
+#define ___PRO_DEF_OPERATOR_DISPATCH_5( \
+    __NAME, __IS_DIRECT, __POS, __SIGN, __DEFFUNC) \
+    ___PRO_DEF_OPERATOR_DISPATCH_IMPL(__NAME, __IS_DIRECT, __POS, __SIGN, \
+        ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC))
+#define PRO_DEF_INDIRECT_OPERATOR_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_OPERATOR_DISPATCH, __NAME, 0, none, \
+        __VA_ARGS__)
+#define PRO_DEF_DIRECT_OPERATOR_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_OPERATOR_DISPATCH, __NAME, 1, none, \
+        __VA_ARGS__)
 #define PRO_DEF_OPERATOR_DISPATCH(__NAME, ...) \
-    ___PRO_EXPAND_MACRO(___PRO_DEF_OPERATOR_DISPATCH, __NAME, none, __VA_ARGS__)
+    PRO_DEF_INDIRECT_OPERATOR_DISPATCH(__NAME, __VA_ARGS__)
+#define PRO_DEF_INDIRECT_PREFIX_OPERATOR_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_OPERATOR_DISPATCH, __NAME, 0, left, \
+        __VA_ARGS__)
+#define PRO_DEF_DIRECT_PREFIX_OPERATOR_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_OPERATOR_DISPATCH, __NAME, 1, left, \
+        __VA_ARGS__)
 #define PRO_DEF_PREFIX_OPERATOR_DISPATCH(__NAME, ...) \
-    ___PRO_EXPAND_MACRO(___PRO_DEF_OPERATOR_DISPATCH, __NAME, left, __VA_ARGS__)
+    PRO_DEF_INDIRECT_PREFIX_OPERATOR_DISPATCH(__NAME, __VA_ARGS__)
+#define PRO_DEF_INDIRECT_POSTFIX_OPERATOR_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_OPERATOR_DISPATCH, __NAME, 0, right, \
+        __VA_ARGS__)
+#define PRO_DEF_DIRECT_POSTFIX_OPERATOR_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_OPERATOR_DISPATCH, __NAME, 1, right, \
+        __VA_ARGS__)
 #define PRO_DEF_POSTFIX_OPERATOR_DISPATCH(__NAME, ...) \
-    ___PRO_EXPAND_MACRO( \
-        ___PRO_DEF_OPERATOR_DISPATCH, __NAME, right, __VA_ARGS__)
+    PRO_DEF_INDIRECT_POSTFIX_OPERATOR_DISPATCH(__NAME, __VA_ARGS__)
 
-#define ___PRO_DEF_CONVERSION_DISPATCH_IMPL(__NAME, __T, ...) \
-    struct __NAME { \
-      __T operator()(auto& __self) \
-          ___PRO_DIRECT_FUNC_IMPL(static_cast<__T>(__self)) \
+#define ___PRO_DEF_CONVERSION_DISPATCH_IMPL(__NAME, __IS_DIRECT, __T, ...) \
+    struct __NAME \
+        : ::pro::details::conversion_dispatch_base<__IS_DIRECT, __T> { \
+      using ::pro::details::conversion_dispatch_base<__IS_DIRECT, __T> \
+          ::operator(); \
       template <class __P> \
-      struct accessor { \
-        template <class __Barrier = void> \
-        explicit operator __T() const \
-            ___PRO_DIRECT_FUNC_IMPL(::pro::proxy_invoke<__NAME>( \
-                static_cast<::pro::details::lazy_eval_t<const __P&, \
-                    __Barrier>>(*this))) \
-      }; \
+      using accessor = ::pro::details::conversion_dispatch_accessor< \
+          __IS_DIRECT, __T, __NAME, __P>; \
       __VA_ARGS__ \
-    }
-#define ___PRO_DEF_CONVERSION_DISPATCH_2(__NAME, __T) \
-    ___PRO_DEF_CONVERSION_DISPATCH_IMPL(__NAME, __T)
-#define ___PRO_DEF_CONVERSION_DISPATCH_3(__NAME, __T, __DEFFUNC) \
+    };
+#define ___PRO_DEF_CONVERSION_DISPATCH_3(__NAME, __IS_DIRECT, __T) \
+    ___PRO_DEF_CONVERSION_DISPATCH_IMPL(__NAME, __IS_DIRECT, __T)
+#define ___PRO_DEF_CONVERSION_DISPATCH_4(__NAME, __IS_DIRECT, __T, __DEFFUNC) \
     ___PRO_DEF_CONVERSION_DISPATCH_IMPL( \
-        __NAME, __T, ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC))
+        __NAME, __IS_DIRECT, __T, ___PRO_DEFAULT_DISPATCH_CALL_IMPL(__DEFFUNC))
+#define PRO_DEF_INDIRECT_CONVERSION_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_CONVERSION_DISPATCH, __NAME, 0, __VA_ARGS__)
+#define PRO_DEF_DIRECT_CONVERSION_DISPATCH(__NAME, ...) \
+    ___PRO_EXPAND_MACRO(___PRO_DEF_CONVERSION_DISPATCH, __NAME, 1, __VA_ARGS__)
 #define PRO_DEF_CONVERSION_DISPATCH(__NAME, ...) \
-    ___PRO_EXPAND_MACRO(___PRO_DEF_CONVERSION_DISPATCH, __NAME, __VA_ARGS__)
+    PRO_DEF_INDIRECT_CONVERSION_DISPATCH(__NAME, __VA_ARGS__)
 
 }  // namespace pro
 
+#undef ___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE
+
 #endif  // _MSFT_PROXY_
diff --git a/samples/resource_dictionary/main.cpp b/samples/resource_dictionary/main.cpp
index c77a4be0..d54a538c 100644
--- a/samples/resource_dictionary/main.cpp
+++ b/samples/resource_dictionary/main.cpp
@@ -15,7 +15,7 @@ struct Dictionary : pro::facade_builder
 }  // namespace spec
 
 void demo_print(pro::proxy<spec::Dictionary> dictionary) {
-  std::cout << dictionary.at(1) << std::endl;
+  std::cout << dictionary->at(1) << std::endl;
 }
 
 int main() {
diff --git a/tests/freestanding/proxy_freestanding_tests.cpp b/tests/freestanding/proxy_freestanding_tests.cpp
index 10a609f2..2cd71225 100644
--- a/tests/freestanding/proxy_freestanding_tests.cpp
+++ b/tests/freestanding/proxy_freestanding_tests.cpp
@@ -31,19 +31,19 @@ extern "C" int main() {
   std::tuple<int, double> t{11, 22};
   pro::proxy<spec::Hashable> p;
   p = &i;
-  if (GetHash(p) != GetHash(i)) {
+  if (GetHash(*p) != GetHash(i)) {
     return 1;
   }
   p = &d;
-  if (GetHash(p) != GetHash(d)) {
+  if (GetHash(*p) != GetHash(d)) {
     return 1;
   }
   p = pro::make_proxy_inplace<spec::Hashable>(s);
-  if (GetHash(p) != GetHash(s)) {
+  if (GetHash(*p) != GetHash(s)) {
     return 1;
   }
   p = &t;
-  if (GetHash(p) != GetDefaultHash()) {
+  if (GetHash(*p) != GetDefaultHash()) {
     return 1;
   }
   return 0;
diff --git a/tests/proxy_creation_tests.cpp b/tests/proxy_creation_tests.cpp
index ce677769..4a5f3749 100644
--- a/tests/proxy_creation_tests.cpp
+++ b/tests/proxy_creation_tests.cpp
@@ -10,6 +10,7 @@ namespace {
 
 struct SboObserver {
  public:
+  static constexpr bool is_direct = true;
   template <class T>
   constexpr explicit SboObserver(std::in_place_type_t<pro::details::inplace_ptr<T>>)
       : SboEnabled(true), AllocatorAllocatesForItself(false) {}
@@ -86,7 +87,7 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_FromValue) {
   {
     auto p = pro::make_proxy_inplace<spec::TestLargeStringable>(session);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -101,7 +102,7 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_InPlace) {
   {
     auto p = pro::make_proxy_inplace<spec::TestLargeStringable, utils::LifetimeTracker::Session>(&tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -116,7 +117,7 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_InPlaceInitializerList) {
   {
     auto p = pro::make_proxy_inplace<spec::TestLargeStringable, utils::LifetimeTracker::Session>({ 1, 2, 3 }, &tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -133,10 +134,10 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_Lifetime_Copy) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     auto p2 = p1;
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 1");
+    ASSERT_EQ(ToString(*p1), "Session 1");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -155,7 +156,7 @@ TEST(ProxyCreationTests, TestMakeProxyInplace_Lifetime_Move) {
     auto p2 = std::move(p1);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
@@ -173,7 +174,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_FromValue) {
   {
     auto p = pro::allocate_proxy<spec::TestSmallStringable>(std::allocator<void>{}, session);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
@@ -189,7 +190,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_InPlace) {
   {
     auto p = pro::allocate_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(std::allocator<void>{}, & tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
@@ -205,7 +206,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_InPlaceInitializerLis
   {
     auto p = pro::allocate_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(std::allocator<void>{}, { 1, 2, 3 }, & tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
@@ -223,11 +224,11 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_Lifetime_Copy) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     auto p2 = p1;
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 1");
+    ASSERT_EQ(ToString(*p1), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).AllocatorAllocatesForItself);
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
@@ -247,7 +248,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_DirectAllocator_Lifetime_Move) {
     auto p2 = std::move(p1);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 1");
+    ASSERT_EQ(ToString(*p2), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -265,7 +266,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_FromValue) {
     std::pmr::unsynchronized_pool_resource memory_pool;
     auto p = pro::allocate_proxy<spec::TestSmallStringable>(std::pmr::polymorphic_allocator<>{&memory_pool}, session);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
@@ -282,7 +283,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_InPlace) {
     std::pmr::unsynchronized_pool_resource memory_pool;
     auto p = pro::allocate_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(std::pmr::polymorphic_allocator<>{&memory_pool}, & tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
@@ -299,7 +300,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_InPlaceInitializerL
     std::pmr::unsynchronized_pool_resource memory_pool;
     auto p = pro::allocate_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(std::pmr::polymorphic_allocator<>{&memory_pool}, { 1, 2, 3 }, & tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
@@ -318,11 +319,11 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_Lifetime_Copy) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     auto p2 = p1;
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 1");
+    ASSERT_EQ(ToString(*p1), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p1).AllocatorAllocatesForItself);
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
@@ -343,7 +344,7 @@ TEST(ProxyCreationTests, TestAllocateProxy_IndirectAllocator_Lifetime_Move) {
     auto p2 = std::move(p1);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 1");
+    ASSERT_EQ(ToString(*p2), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -360,7 +361,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_FromValue) {
   {
     auto p = pro::make_proxy<spec::TestLargeStringable>(session);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -375,7 +376,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_InPlace) {
   {
     auto p = pro::make_proxy<spec::TestLargeStringable, utils::LifetimeTracker::Session>(&tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -390,7 +391,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_InPlaceInitializerList) {
   {
     auto p = pro::make_proxy<spec::TestLargeStringable, utils::LifetimeTracker::Session>({ 1, 2, 3 }, &tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -407,10 +408,10 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_Lifetime_Copy) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     auto p2 = p1;
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 1");
+    ASSERT_EQ(ToString(*p1), "Session 1");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -429,7 +430,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_Lifetime_Move) {
     auto p2 = std::move(p1);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     ASSERT_TRUE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
@@ -447,7 +448,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_FromValue) {
   {
     auto p = pro::make_proxy<spec::TestSmallStringable>(session);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
@@ -463,7 +464,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_InPlace) {
   {
     auto p = pro::make_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>(&tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
@@ -479,7 +480,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_InPlaceInitializerList) {
   {
     auto p = pro::make_proxy<spec::TestSmallStringable, utils::LifetimeTracker::Session>({ 1, 2, 3 }, &tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p).AllocatorAllocatesForItself);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
@@ -497,11 +498,11 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_Lifetime_Copy) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     auto p2 = p1;
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 1");
+    ASSERT_EQ(ToString(*p1), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p1).AllocatorAllocatesForItself);
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
@@ -521,7 +522,7 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_Lifetime_Move) {
     auto p2 = std::move(p1);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 1");
+    ASSERT_EQ(ToString(*p2), "Session 1");
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).SboEnabled);
     ASSERT_FALSE(pro::proxy_reflect<SboObserver>(p2).AllocatorAllocatesForItself);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp
index 89a09568..31f3eb23 100644
--- a/tests/proxy_dispatch_tests.cpp
+++ b/tests/proxy_dispatch_tests.cpp
@@ -47,8 +47,7 @@ PRO_DEF_OPERATOR_DISPATCH(OpBitwiseXorAssignment, "^=");
 PRO_DEF_OPERATOR_DISPATCH(OpLeftShiftAssignment, "<<=");
 PRO_DEF_OPERATOR_DISPATCH(OpRightShiftAssignment, ">>=");
 PRO_DEF_OPERATOR_DISPATCH(OpComma, ",");
-PRO_DEF_OPERATOR_DISPATCH(OpPtrToMem, "->*");
-PRO_DEF_OPERATOR_DISPATCH(OpArrow, "->");
+PRO_DEF_DIRECT_OPERATOR_DISPATCH(OpPtrToMem, "->*");
 PRO_DEF_OPERATOR_DISPATCH(OpParentheses, "()");
 PRO_DEF_OPERATOR_DISPATCH(OpBrackets, "[]");
 
@@ -77,6 +76,8 @@ PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpComma, ",");
 PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpPtrToMem, "->*");
 
 PRO_DEF_CONVERSION_DISPATCH(ConvertToInt, int);
+template <class F>
+PRO_DEF_DIRECT_CONVERSION_DISPATCH(ConvertToBase, pro::proxy<F>);
 
 struct CommaTester {
 public:
@@ -102,42 +103,42 @@ TEST(ProxyDispatchTests, TestOpPlus) {
   struct TestFacade : pro::facade_builder::add_convention<OpPlus, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p + 2, 14);
+  ASSERT_EQ(*p + 2, 14);
 }
 
 TEST(ProxyDispatchTests, TestOpMinus) {
   struct TestFacade : pro::facade_builder::add_convention<OpMinus, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p - 2, 10);
+  ASSERT_EQ(*p - 2, 10);
 }
 
 TEST(ProxyDispatchTests, TestOpAsterisk) {
   struct TestFacade : pro::facade_builder::add_convention<OpAsterisk, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p * 2, 24);
+  ASSERT_EQ(*p * 2, 24);
 }
 
 TEST(ProxyDispatchTests, TestOpSlash) {
   struct TestFacade : pro::facade_builder::add_convention<OpSlash, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p / 2, 6);
+  ASSERT_EQ(*p / 2, 6);
 }
 
 TEST(ProxyDispatchTests, TestOpPercent) {
   struct TestFacade : pro::facade_builder::add_convention<OpPercent, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p % 5, 2);
+  ASSERT_EQ(*p % 5, 2);
 }
 
 TEST(ProxyDispatchTests, TestOpIncrement) {
   struct TestFacade : pro::facade_builder::add_convention<OpIncrement, int()>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p++, 12);
+  ASSERT_EQ((*p)++, 12);
   ASSERT_EQ(v, 13);
 }
 
@@ -145,7 +146,7 @@ TEST(ProxyDispatchTests, TestOpDecrement) {
   struct TestFacade : pro::facade_builder::add_convention<OpDecrement, int()>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p--, 12);
+  ASSERT_EQ((*p)--, 12);
   ASSERT_EQ(v, 11);
 }
 
@@ -153,202 +154,202 @@ TEST(ProxyDispatchTests, TestOpEqualTo) {
   struct TestFacade : pro::facade_builder::add_convention<OpEqualTo, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p == 12, true);
-  ASSERT_EQ(p == 11, false);
+  ASSERT_EQ(*p == 12, true);
+  ASSERT_EQ(*p == 11, false);
 }
 
 TEST(ProxyDispatchTests, TestOpNotEqualTo) {
   struct TestFacade : pro::facade_builder::add_convention<OpNotEqualTo, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p != 12, false);
-  ASSERT_EQ(p != 11, true);
+  ASSERT_EQ(*p != 12, false);
+  ASSERT_EQ(*p != 11, true);
 }
 
 TEST(ProxyDispatchTests, TestOpGreaterThan) {
   struct TestFacade : pro::facade_builder::add_convention<OpGreaterThan, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p > 2, true);
-  ASSERT_EQ(p > 20, false);
+  ASSERT_EQ(*p > 2, true);
+  ASSERT_EQ(*p > 20, false);
 }
 
 TEST(ProxyDispatchTests, TestOpLessThan) {
   struct TestFacade : pro::facade_builder::add_convention<OpLessThan, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p < 2, false);
-  ASSERT_EQ(p < 20, true);
+  ASSERT_EQ(*p < 2, false);
+  ASSERT_EQ(*p < 20, true);
 }
 
 TEST(ProxyDispatchTests, TestOpGreaterThanOrEqualTo) {
   struct TestFacade : pro::facade_builder::add_convention<OpGreaterThanOrEqualTo, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p >= 20, false);
-  ASSERT_EQ(p >= 12, true);
+  ASSERT_EQ(*p >= 20, false);
+  ASSERT_EQ(*p >= 12, true);
 }
 
 TEST(ProxyDispatchTests, TestOpLessThanOrEqualTo) {
   struct TestFacade : pro::facade_builder::add_convention<OpLessThanOrEqualTo, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p <= 2, false);
-  ASSERT_EQ(p <= 12, true);
+  ASSERT_EQ(*p <= 2, false);
+  ASSERT_EQ(*p <= 12, true);
 }
 
 TEST(ProxyDispatchTests, TestOpSpaceship) {
   struct TestFacade : pro::facade_builder::add_convention<OpSpaceship, std::strong_ordering(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p <=> 2, std::strong_ordering::greater);
-  ASSERT_EQ(p <=> 12, std::strong_ordering::equal);
-  ASSERT_EQ(p <=> 20, std::strong_ordering::less);
+  ASSERT_EQ(*p <=> 2, std::strong_ordering::greater);
+  ASSERT_EQ(*p <=> 12, std::strong_ordering::equal);
+  ASSERT_EQ(*p <=> 20, std::strong_ordering::less);
 }
 
 TEST(ProxyDispatchTests, TestOpLogicalNot) {
   struct TestFacade : pro::facade_builder::add_convention<OpLogicalNot, bool()>::build {};
   int v1 = 12, v2 = 0;
   pro::proxy<TestFacade> p1 = &v1, p2 = &v2;
-  ASSERT_EQ(!p1, false);
-  ASSERT_EQ(!p2, true);
+  ASSERT_EQ(!*p1, false);
+  ASSERT_EQ(!*p2, true);
 }
 
 TEST(ProxyDispatchTests, TestOpLogicalAnd) {
   struct TestFacade : pro::facade_builder::add_convention<OpLogicalAnd, bool(bool val)>::build {};
   bool v = true;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p && true, true);
-  ASSERT_EQ(p && false, false);
+  ASSERT_EQ(*p && true, true);
+  ASSERT_EQ(*p && false, false);
 }
 
 TEST(ProxyDispatchTests, TestOpLogicalOr) {
   struct TestFacade : pro::facade_builder::add_convention<OpLogicalOr, bool(bool val)>::build {};
   bool v = false;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(v || true, true);
-  ASSERT_EQ(v || false, false);
+  ASSERT_EQ(*p || true, true);
+  ASSERT_EQ(*p || false, false);
 }
 
 TEST(ProxyDispatchTests, TestOpTilde) {
   struct TestFacade : pro::facade_builder::add_convention<OpTilde, int()>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(~v, -13);
+  ASSERT_EQ(~*p, -13);
 }
 
 TEST(ProxyDispatchTests, TestOpAmpersand) {
   struct TestFacade : pro::facade_builder::add_convention<OpAmpersand, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p & 4, 4);
+  ASSERT_EQ(*p & 4, 4);
 }
 
 TEST(ProxyDispatchTests, TestOpPipe) {
   struct TestFacade : pro::facade_builder::add_convention<OpPipe, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p | 6, 14);
+  ASSERT_EQ(*p | 6, 14);
 }
 
 TEST(ProxyDispatchTests, TestOpCaret) {
   struct TestFacade : pro::facade_builder::add_convention<OpCaret, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p ^ 5, 9);
+  ASSERT_EQ(*p ^ 5, 9);
 }
 
 TEST(ProxyDispatchTests, TestOpLeftShift) {
   struct TestFacade : pro::facade_builder::add_convention<OpLeftShift, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p << 2, 48);
+  ASSERT_EQ(*p << 2, 48);
 }
 
 TEST(ProxyDispatchTests, TestOpRightShift) {
   struct TestFacade : pro::facade_builder::add_convention<OpRightShift, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p >> 2, 3);
+  ASSERT_EQ(*p >> 2, 3);
 }
 
 TEST(ProxyDispatchTests, TestOpPlusAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpPlusAssignment, void(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p += 2).has_value());
-  ASSERT_EQ(v, 14);
+  (*p += 2) += 3;
+  ASSERT_EQ(v, 17);
 }
 
 TEST(ProxyDispatchTests, TestOpMinusAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpMinusAssignment, void(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p -= 2).has_value());
-  ASSERT_EQ(v, 10);
+  (*p -= 2) -= 3;
+  ASSERT_EQ(v, 7);
 }
 
 TEST(ProxyDispatchTests, TestOpMultiplicationAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpMultiplicationAssignment, void(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p *= 2).has_value());
-  ASSERT_EQ(v, 24);
+  (*p *= 2) *= 3;
+  ASSERT_EQ(v, 72);
 }
 
 TEST(ProxyDispatchTests, TestOpDivisionAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpDivisionAssignment, void(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p /= 2).has_value());
-  ASSERT_EQ(v, 6);
+  (*p /= 2) /= 2;
+  ASSERT_EQ(v, 3);
 }
 
 TEST(ProxyDispatchTests, TestOpBitwiseAndAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpBitwiseAndAssignment, void(int val)>::build {};
-  int v = 12;
+  int v = 15;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p &= 6).has_value());
-  ASSERT_EQ(v, 4);
+  (*p &= 11) &= 14;
+  ASSERT_EQ(v, 10);
 }
 
 TEST(ProxyDispatchTests, TestOpBitwiseOrAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpBitwiseOrAssignment, void(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p |= 2).has_value());
-  ASSERT_EQ(v, 14);
+  (*p |= 2) |= 1;
+  ASSERT_EQ(v, 15);
 }
 
 TEST(ProxyDispatchTests, TestOpBitwiseXorAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpBitwiseXorAssignment, void(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p ^= 6).has_value());
-  ASSERT_EQ(v, 10);
+  (*p ^= 6) ^= 1;
+  ASSERT_EQ(v, 11);
 }
 
 TEST(ProxyDispatchTests, TestOpLeftShiftAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpLeftShiftAssignment, void(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p <<= 2).has_value());
-  ASSERT_EQ(v, 48);
+  (*p <<= 2) <<= 1;
+  ASSERT_EQ(v, 96);
 }
 
 TEST(ProxyDispatchTests, TestOpRightShiftAssignment) {
   struct TestFacade : pro::facade_builder::add_convention<OpRightShiftAssignment, void(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_TRUE((p >>= 2).has_value());
-  ASSERT_EQ(v, 3);
+  (*p >>= 2) >>= 1;
+  ASSERT_EQ(v, 1);
 }
 
 TEST(ProxyDispatchTests, TestOpComma) {
   struct TestFacade : pro::facade_builder::add_convention<OpComma, int(int val)>::build {};
   CommaTester v{3};
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ((p, 6), 9);
+  ASSERT_EQ((*p, 6), 9);
 }
 
 TEST(ProxyDispatchTests, TestOpPtrToMem) {
@@ -373,25 +374,18 @@ TEST(ProxyDispatchTests, TestOpPtrToMem) {
   ASSERT_EQ(v2.c, 3);
 }
 
-TEST(ProxyDispatchTests, TestOpArrow) {
-  struct TestFacade : pro::facade_builder::add_convention<OpArrow, const void*()>::build {};
-  int v = 12;
-  pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(std::to_address(p), &v);
-}
-
 TEST(ProxyDispatchTests, TestOpParentheses) {
   struct TestFacade : pro::facade_builder::add_convention<OpParentheses, int(int a, int b)>::build {};
   auto v = [](auto&&... args) { return (args + ...); };
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(p(2, 3), 5);
+  ASSERT_EQ((*p)(2, 3), 5);
 }
 
 TEST(ProxyDispatchTests, TestOpBrackets) {
   struct TestFacade : pro::facade_builder::add_convention<OpBrackets, int&(int idx)>::build {};
   std::unordered_map<int, int> v;
   pro::proxy<TestFacade> p = &v;
-  p[3] = 12;
+  (*p)[3] = 12;
   ASSERT_EQ(v.size(), 1u);
   ASSERT_EQ(v.at(3), 12);
 }
@@ -400,45 +394,44 @@ TEST(ProxyDispatchTests, TestPreOpPlus) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpPlus, int(), int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(+p, 12);
-  ASSERT_EQ(2 + p, 14);
+  ASSERT_EQ(+*p, 12);
+  ASSERT_EQ(2 + *p, 14);
 }
 
 TEST(ProxyDispatchTests, TestPreOpMinus) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpMinus, int(), int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(-p, -12);
-  ASSERT_EQ(2 - p, -10);
+  ASSERT_EQ(-*p, -12);
+  ASSERT_EQ(2 - *p, -10);
 }
 
 TEST(ProxyDispatchTests, TestPreOpAsterisk) {
-  struct TestFacade : pro::facade_builder::add_convention<PreOpAsterisk, int(), int(int val)>::build {};
+  struct TestFacade : pro::facade_builder::add_convention<PreOpAsterisk, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(*p, 12);
-  ASSERT_EQ(2 * p, 24);
+  ASSERT_EQ(2 * *p, 24);
 }
 
 TEST(ProxyDispatchTests, TestPreOpSlash) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpSlash, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(50 / p, 4);
+  ASSERT_EQ(50 / *p, 4);
 }
 
 TEST(ProxyDispatchTests, TestPreOpPercent) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpPercent, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(26 % p, 2);
+  ASSERT_EQ(26 % *p, 2);
 }
 
 TEST(ProxyDispatchTests, TestPreOpIncrement) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpIncrement, int()>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(++p, 13);
+  ASSERT_EQ(++*p, 13);
   ASSERT_EQ(v, 13);
 }
 
@@ -446,7 +439,7 @@ TEST(ProxyDispatchTests, TestPreOpDecrement) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpDecrement, int()>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(--p, 11);
+  ASSERT_EQ(--*p, 11);
   ASSERT_EQ(v, 11);
 }
 
@@ -454,104 +447,104 @@ TEST(ProxyDispatchTests, TestPreOpEqualTo) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpEqualTo, bool(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(2 == p, false);
-  ASSERT_EQ(12 == p, true);
+  ASSERT_EQ(2 == *p, false);
+  ASSERT_EQ(12 == *p, true);
 }
 
 TEST(ProxyDispatchTests, TestPreOpNotEqualTo) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpNotEqualTo, bool(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(2 != p, true);
-  ASSERT_EQ(12 != p, false);
+  ASSERT_EQ(2 != *p, true);
+  ASSERT_EQ(12 != *p, false);
 }
 
 TEST(ProxyDispatchTests, TestPreOpGreaterThan) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpGreaterThan, bool(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(12 > p, false);
-  ASSERT_EQ(13 > p, true);
+  ASSERT_EQ(12 > *p, false);
+  ASSERT_EQ(13 > *p, true);
 }
 
 TEST(ProxyDispatchTests, TestPreOpLessThan) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpLessThan, bool(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(12 < p, false);
-  ASSERT_EQ(11 < p, true);
+  ASSERT_EQ(12 < *p, false);
+  ASSERT_EQ(11 < *p, true);
 }
 
 TEST(ProxyDispatchTests, TestPreOpGreaterThanOrEqualTo) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpGreaterThanOrEqualTo, bool(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(11 >= p, false);
-  ASSERT_EQ(12 >= p, true);
+  ASSERT_EQ(11 >= *p, false);
+  ASSERT_EQ(12 >= *p, true);
 }
 
 TEST(ProxyDispatchTests, TestPreOpLessThanOrEqualTo) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpLessThanOrEqualTo, bool(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(13 <= p, false);
-  ASSERT_EQ(12 <= p, true);
+  ASSERT_EQ(13 <= *p, false);
+  ASSERT_EQ(12 <= *p, true);
 }
 
 TEST(ProxyDispatchTests, TestPreOpSpaceship) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpSpaceship, std::strong_ordering(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(2 <=> p, std::strong_ordering::less);
-  ASSERT_EQ(12 <=> p, std::strong_ordering::equal);
-  ASSERT_EQ(20 <=> p, std::strong_ordering::greater);
+  ASSERT_EQ(2 <=> *p, std::strong_ordering::less);
+  ASSERT_EQ(12 <=> *p, std::strong_ordering::equal);
+  ASSERT_EQ(20 <=> *p, std::strong_ordering::greater);
 }
 
 TEST(ProxyDispatchTests, TestPreOpLogicalAnd) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpLogicalAnd, bool(bool val)>::build {};
   bool v = true;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(true && p, true);
-  ASSERT_EQ(false && p, false);
+  ASSERT_EQ(true && *p, true);
+  ASSERT_EQ(false && *p, false);
 }
 
 TEST(ProxyDispatchTests, TestPreOpLogicalOr) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpLogicalOr, bool(bool val)>::build {};
   bool v = false;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(false || p, false);
-  ASSERT_EQ(true || p, true);
+  ASSERT_EQ(false || *p, false);
+  ASSERT_EQ(true || *p, true);
 }
 
 TEST(ProxyDispatchTests, TestPreOpAmpersand) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpAmpersand, const void*() noexcept, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(6 & p, 4);
-  ASSERT_EQ(&p, &v);
+  ASSERT_EQ(6 & *p, 4);
+  ASSERT_EQ(&*p, &v);
 }
 
 TEST(ProxyDispatchTests, TestPreOpPipe) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpPipe, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(6 | p, 14);
+  ASSERT_EQ(6 | *p, 14);
 }
 
 TEST(ProxyDispatchTests, TestPreOpCaret) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpCaret, int(int val)>::build {};
   int v = 12;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(8 ^ p, 4);
+  ASSERT_EQ(8 ^ *p, 4);
 }
 
 TEST(ProxyDispatchTests, TestPreOpLeftShift) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpLeftShift, int(int val), std::ostream&(std::ostream& out)>::build {};
   int v = 2;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(12 << p, 48);
+  ASSERT_EQ(12 << *p, 48);
   std::ostringstream stream;
-  stream << p;
+  stream << *p;
   ASSERT_EQ(stream.str(), "2");
 }
 
@@ -559,9 +552,9 @@ TEST(ProxyDispatchTests, TestPreOpRightShift) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpRightShift, int(int val), std::istream&(std::istream& in)>::build {};
   int v = 1;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(25 >> p, 12);
+  ASSERT_EQ(25 >> *p, 12);
   std::istringstream stream("123");
-  stream >> p;
+  stream >> *p;
   ASSERT_EQ(v, 123);
 }
 
@@ -569,19 +562,36 @@ TEST(ProxyDispatchTests, TestPreOpComma) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpComma, int(int val)>::build {};
   CommaTester v{3};
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ((7, p), 21);
+  ASSERT_EQ((7, *p), 21);
 }
 
 TEST(ProxyDispatchTests, TestPreOpPtrToMem) {
   struct TestFacade : pro::facade_builder::add_convention<PreOpPtrToMem, int(int val)>::build {};
   PtrToMemTester v{3};
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(2->*p, 6);
+  ASSERT_EQ(2->**p, 6);
 }
 
-TEST(ProxyDispatchTests, TestConversion) {
+TEST(ProxyDispatchTests, TestIndirectConversion) {
   struct TestFacade : pro::facade_builder::add_convention<ConvertToInt, int()>::build {};
   double v = 12.3;
   pro::proxy<TestFacade> p = &v;
-  ASSERT_EQ(static_cast<int>(p), 12);
+  ASSERT_EQ(static_cast<int>(*p), 12);
+}
+
+TEST(ProxyDispatchTests, TestDirectConversion) {
+  struct TestFacadeBase : pro::facade_builder
+      ::add_convention<PreOpLeftShift, std::ostream&(std::ostream& out)>
+      ::build {};
+  struct TestFacade : pro::facade_builder
+      ::add_facade<TestFacadeBase>
+      ::add_convention<OpPlusAssignment, void(int val)>
+      ::add_convention<ConvertToBase<TestFacadeBase>, pro::proxy<TestFacadeBase>() &&>
+      ::build {};
+  pro::proxy<TestFacade> p1 = std::make_unique<int>(123);
+  *p1 += 3;
+  pro::proxy<TestFacadeBase> p2 = static_cast<pro::proxy<TestFacadeBase>>(std::move(p1));
+  std::ostringstream stream;
+  stream << *p2;
+  ASSERT_EQ(stream.str(), "126");
 }
diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp
index a9beb499..cb3d5feb 100644
--- a/tests/proxy_integration_tests.cpp
+++ b/tests/proxy_integration_tests.cpp
@@ -64,8 +64,8 @@ class Point {
 std::string PrintDrawableToString(pro::proxy<spec::Drawable> p) {
   std::stringstream result;
   result << std::fixed << std::setprecision(5) << "shape = ";
-  p.Draw(result);
-  result << ", area = " << p.Area();
+  p->Draw(result);
+  result << ", area = " << p->Area();
   return std::move(result).str();
 }
 
@@ -157,11 +157,11 @@ TEST(ProxyIntegrationTests, TestDrawable) {
 TEST(ProxyIntegrationTests, TestLogger) {
   std::ostringstream out;
   auto logger = pro::make_proxy<spec::Logger, StreamLogger>(out);
-  logger.Log("hello");
+  logger->Log("hello");
   try {
     throw std::runtime_error{"runtime error!"};
   } catch (const std::exception& e) {
-    logger.Log("world", e);
+    logger->Log("world", e);
   }
   auto content = std::move(out).str();
   ASSERT_EQ(content, "[INFO] hello\n\
diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp
index 7e4acf43..c6dddef5 100644
--- a/tests/proxy_invocation_tests.cpp
+++ b/tests/proxy_invocation_tests.cpp
@@ -3,6 +3,7 @@
 
 #include <gtest/gtest.h>
 #include <algorithm>
+#include <functional>
 #include <list>
 #include <map>
 #include <ranges>
@@ -49,7 +50,7 @@ PRO_DEF_FREE_DISPATCH(FreeForEach, std::ranges::for_each, ForEach);
 
 template <class T>
 struct Iterable : pro::facade_builder
-    ::add_convention<FreeForEach, void(pro::proxy<Callable<void(T&)>>)>
+    ::add_convention<FreeForEach, void(std::function<void(T&)>)>
     ::template add_convention<FreeSize, std::size_t() noexcept>
     ::build {};
 
@@ -97,11 +98,11 @@ template <class F, class T>
 auto GetWeakImpl(const std::shared_ptr<T>& p) { return pro::make_proxy<Weak<F>, std::weak_ptr<T>>(p); }
 
 template <class F>
-PRO_DEF_FREE_DISPATCH(FreeGetWeak, GetWeakImpl<F>, GetWeak, std::nullptr_t);
+PRO_DEF_DIRECT_FREE_DISPATCH(FreeGetWeak, GetWeakImpl<F>, GetWeak, std::nullptr_t);
 
 struct SharedStringable : pro::facade_builder
     ::add_facade<utils::spec::Stringable>
-    ::add_convention<FreeGetWeak<SharedStringable>, pro::proxy<Weak<SharedStringable>>()>
+    ::add_convention<FreeGetWeak<SharedStringable>, pro::proxy<Weak<SharedStringable>>() const&>
     ::build {};
 
 }  // namespace spec
@@ -115,8 +116,8 @@ concept InvocableWithDispatch =
 template <class F, bool NE, class... Args>
 concept InvocableWithoutDispatch =
   requires(const pro::proxy<F> p, Args... args) {
-    { p(std::forward<Args>(args)...) };
-    typename std::enable_if_t<NE == noexcept(p(std::forward<Args>(args)...))>;
+    { (*p)(std::forward<Args>(args)...) };
+    typename std::enable_if_t<NE == noexcept((*p)(std::forward<Args>(args)...))>;
 };
 
 // Static assertions for a facade of a single dispatch
@@ -153,7 +154,7 @@ TEST(ProxyInvocationTests, TestArgumentForwarding) {
     return expected_result;
   };
   pro::proxy<spec::Callable<int(std::string, std::vector<int>)>> p = &f;
-  int result = p(arg1, std::move(arg2));
+  int result = (*p)(arg1, std::move(arg2));
   ASSERT_TRUE(p.has_value());
   ASSERT_EQ(arg1_received, arg1);
   ASSERT_TRUE(arg2.empty());
@@ -167,7 +168,7 @@ TEST(ProxyInvocationTests, TestThrow) {
   bool exception_thrown = false;
   pro::proxy<spec::Callable<void()>> p = &f;
   try {
-    p();
+    (*p)();
   } catch (const std::runtime_error& e) {
     exception_thrown = true;
     ASSERT_STREQ(e.what(), expected_error_message);
@@ -179,42 +180,42 @@ TEST(ProxyInvocationTests, TestThrow) {
 TEST(ProxyInvocationTests, TestMultipleDispatches_Unique) {
   std::list<int> l = { 1, 2, 3 };
   pro::proxy<spec::Iterable<int>> p = &l;
-  ASSERT_EQ(Size(p), 3);
+  ASSERT_EQ(Size(*p), 3);
   int sum = 0;
   auto accumulate_sum = [&](int x) { sum += x; };
-  ForEach(p, &accumulate_sum);
+  ForEach(*p, accumulate_sum);
   ASSERT_EQ(sum, 6);
 }
 
 TEST(ProxyInvocationTests, TestMultipleDispatches_Duplicated) {
   struct DuplicatedIterable : pro::facade_builder
-      ::add_convention<spec::FreeForEach, void(pro::proxy<spec::Callable<void(int&)>>)>
+      ::add_convention<spec::FreeForEach, void(std::function<void(int&)>)>
       ::add_convention<spec::FreeSize, std::size_t()>
-      ::add_convention<spec::FreeForEach, void(pro::proxy<spec::Callable<void(int&)>>)>
+      ::add_convention<spec::FreeForEach, void(std::function<void(int&)>)>
       ::build {};
   static_assert(sizeof(pro::details::facade_traits<DuplicatedIterable>::meta) ==
       sizeof(pro::details::facade_traits<spec::Iterable<int>>::meta));
   std::list<int> l = { 1, 2, 3 };
   pro::proxy<DuplicatedIterable> p = &l;
-  ASSERT_EQ(Size(p), 3);
+  ASSERT_EQ(Size(*p), 3);
   int sum = 0;
   auto accumulate_sum = [&](int x) { sum += x; };
-  ForEach(p, &accumulate_sum);
+  ForEach(*p, accumulate_sum);
   ASSERT_EQ(sum, 6);
 }
 
 TEST(ProxyInvocationTests, TestRecursiveDefinition) {
   std::list<int> l = { 1, 2, 3 };
   pro::proxy<spec::Container<int>> p = &l;
-  ASSERT_EQ(Size(p), 3);
+  ASSERT_EQ(Size(*p), 3);
   int sum = 0;
   auto accumulate_sum = [&](int x) { sum += x; };
-  ForEach(p, &accumulate_sum);
+  ForEach(*p, accumulate_sum);
   ASSERT_EQ(sum, 6);
-  Append(Append(Append(p, 4), 5), 6);
-  ASSERT_EQ(Size(p), 6);
+  Append(*Append(*Append(*p, 4), 5), 6);
+  ASSERT_EQ(Size(*p), 6);
   sum = 0;
-  ForEach(p, &accumulate_sum);
+  ForEach(*p, accumulate_sum);
   ASSERT_EQ(sum, 21);
 }
 
@@ -225,37 +226,37 @@ TEST(ProxyInvocationTests, TestOverloadResolution) {
   std::vector<std::type_index> side_effect;
   auto p = pro::make_proxy<OverloadedCallable>([&](auto&&... args)
       { side_effect = GetTypeIndices<std::decay_t<decltype(args)>...>(); });
-  p(123);
+  (*p)(123);
   ASSERT_EQ(side_effect, GetTypeIndices<int>());
-  p(1.23);
+  (*p)(1.23);
   ASSERT_EQ(side_effect, GetTypeIndices<double>());
   char foo[2];
-  p(foo);
+  (*p)(foo);
   ASSERT_EQ(side_effect, GetTypeIndices<char*>());
-  p("lalala");
+  (*p)("lalala");
   ASSERT_EQ(side_effect, GetTypeIndices<const char*>());
-  p("lalala", 0);
+  (*p)("lalala", 0);
   ASSERT_EQ(side_effect, (GetTypeIndices<std::string, int>()));
-  ASSERT_FALSE((std::is_invocable_v<decltype(p), std::vector<int>>));
+  ASSERT_FALSE((std::is_invocable_v<decltype(*p), std::vector<int>>));
 }
 
 TEST(ProxyInvocationTests, TestNoexcept) {
   std::vector<std::type_index> side_effect;
   auto p = pro::make_proxy<spec::Callable<void(int) noexcept, void(double)>>([&](auto&&... args) noexcept
     { side_effect = GetTypeIndices<std::decay_t<decltype(args)>...>(); });
-  static_assert(noexcept(p(123)));
-  p(123);
+  static_assert(noexcept((*p)(123)));
+  (*p)(123);
   ASSERT_EQ(side_effect, GetTypeIndices<int>());
-  static_assert(!noexcept(p(1.23)));
-  p(1.23);
+  static_assert(!noexcept((*p)(1.23)));
+  (*p)(1.23);
   ASSERT_EQ(side_effect, GetTypeIndices<double>());
-  ASSERT_FALSE((std::is_invocable_v<decltype(p), char*>));
+  ASSERT_FALSE((std::is_invocable_v<decltype(*p), char*>));
 }
 
 TEST(ProxyInvocationTests, TestFunctionPointer) {
   struct TestFacade : spec::Callable<std::vector<std::type_index>()> {};
   pro::proxy<TestFacade> p{ &GetTypeIndices<int, double> };
-  auto ret = p();
+  auto ret = (*p)();
   ASSERT_EQ(ret, (GetTypeIndices<int, double>()));
 }
 
@@ -263,12 +264,12 @@ TEST(ProxyInvocationTests, TestMemberDispatchDefault) {
   std::vector<std::string> container1{ "hello", "world", "!"};
   std::list<std::string> container2{ "hello", "world" };
   pro::proxy<spec::ResourceDictionary> p = &container1;
-  ASSERT_EQ(p.at(0), "hello");
+  ASSERT_EQ(p->at(0), "hello");
   p = &container2;
   {
     bool exception_thrown = false;
     try {
-      p.at(0);
+      p->at(0);
     } catch (const std::runtime_error& e) {
       exception_thrown = true;
       ASSERT_EQ(static_cast<std::string>(e.what()), "Not implemented!");
@@ -281,14 +282,14 @@ TEST(ProxyInvocationTests, TestFreeDispatchDefault) {
   {
     int side_effect = 0;
     auto p = pro::make_proxy<spec::WeakCallable<void()>>([&] { side_effect = 1; });
-    p();
+    (*p)();
     ASSERT_EQ(side_effect, 1);
   }
   {
     bool exception_thrown = false;
     auto p = pro::make_proxy<spec::WeakCallable<void()>>(123);
     try {
-      p();
+      (*p)();
     } catch (const std::runtime_error& e) {
       exception_thrown = true;
       ASSERT_EQ(static_cast<std::string>(e.what()), "Not implemented!");
@@ -303,24 +304,13 @@ TEST(ProxyInvocationTests, TestObserverDispatch) {
   auto weak = GetWeak(p);
   ASSERT_TRUE(weak.has_value());
   {
-    auto locked = Lock(weak);
+    auto locked = Lock(*weak);
     ASSERT_TRUE(locked.has_value());
-    ASSERT_EQ(ToString(locked), "123");
+    ASSERT_EQ(ToString(*locked), "123");
   }
   p = &test_val;  // The underlying std::shared_ptr will be destroyed
   ASSERT_TRUE(weak.has_value());
-  ASSERT_FALSE(Lock(weak).has_value());
+  ASSERT_FALSE(Lock(*weak).has_value());
   ASSERT_FALSE(GetWeak(p).has_value());
-  ASSERT_EQ(ToString(p), "123");
-}
-
-TEST(ProxyInvocationTests, TestPtrToMember) {
-  using Runnable = pro::proxy<spec::Callable<void()>>;
-  constexpr auto OpCallPtr = Runnable::get_dispatch_ptr<spec::OpCall>();
-  static_assert(std::is_same_v<decltype(OpCallPtr), const Runnable::dispatch_ptr<void()>>);
-  int side_effects = 0;
-  auto f = [&] { side_effects = 1; };
-  Runnable p = &f;
-  (p->*OpCallPtr)();
-  ASSERT_EQ(side_effects, 1);
+  ASSERT_EQ(ToString(*p), "123");
 }
diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp
index 4aae22d0..074c6c83 100644
--- a/tests/proxy_lifetime_tests.cpp
+++ b/tests/proxy_lifetime_tests.cpp
@@ -37,7 +37,7 @@ TEST(ProxyLifetimeTests, TestPolyConstrction_FromValue) {
   {
     pro::proxy<TestFacade> p = utils::LifetimeTracker::Session(&tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
@@ -74,7 +74,7 @@ TEST(ProxyLifetimeTests, TestPolyConstrction_InPlace) {
   {
     pro::proxy<TestFacade> p{ std::in_place_type<utils::LifetimeTracker::Session>, &tracker };
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
@@ -106,7 +106,7 @@ TEST(ProxyLifetimeTests, TestPolyConstrction_InPlaceInitializerList) {
   {
     pro::proxy<TestFacade> p{ std::in_place_type<utils::LifetimeTracker::Session>, { 1, 2, 3 }, &tracker };
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
@@ -140,9 +140,9 @@ TEST(ProxyLifetimeTests, TestCopyConstrction_FromValue) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     auto p2 = p1;
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 1");
+    ASSERT_EQ(ToString(*p1), "Session 1");
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
@@ -167,7 +167,7 @@ TEST(ProxyLifetimeTests, TestCopyConstrction_FromValue_Exception) {
     }
     ASSERT_TRUE(exception_thrown);
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 1");
+    ASSERT_EQ(ToString(*p1), "Session 1");
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
   expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
@@ -190,7 +190,7 @@ TEST(ProxyLifetimeTests, TestMoveConstrction_FromValue) {
     auto p2 = std::move(p1);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -210,7 +210,7 @@ TEST(ProxyLifetimeTests, TestMoveConstrction_FromValue_Trivial) {
     auto p2 = std::move(p1);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 1");
+    ASSERT_EQ(ToString(*p2), "Session 1");
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
   expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
@@ -252,7 +252,7 @@ TEST(ProxyLifetimeTests, TestPolyAssignment_ToValue) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     p = utils::LifetimeTracker::Session{ &tracker };
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 3");
+    ASSERT_EQ(ToString(*p), "Session 3");
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kValueConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     expected_ops.emplace_back(3, utils::LifetimeOperationType::kMoveConstruction);
@@ -281,7 +281,7 @@ TEST(ProxyLifetimeTests, TestPolyAssignment_ToValue_Exception) {
     }
     ASSERT_TRUE(exception_thrown);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
   expected_ops.emplace_back(2, utils::LifetimeOperationType::kDestruction);
@@ -296,7 +296,7 @@ TEST(ProxyLifetimeTests, TestPolyAssignment_FromValue_ToNull) {
     pro::proxy<TestFacade> p;
     p = utils::LifetimeTracker::Session{ &tracker };
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
@@ -337,7 +337,7 @@ TEST(ProxyLifetimeTests, TestPolyAssignment_InPlace_ToValue) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     p.emplace<utils::LifetimeTracker::Session>(&tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kValueConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -375,7 +375,7 @@ TEST(ProxyLifetimeTests, TestPolyAssignment_InPlace_ToNull) {
     pro::proxy<TestFacade> p;
     p.emplace<utils::LifetimeTracker::Session>(&tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
@@ -411,7 +411,7 @@ TEST(ProxyLifetimeTests, TestPolyAssignment_InPlaceInitializerList_ToValue) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     p.emplace<utils::LifetimeTracker::Session>({ 1, 2, 3 }, &tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 2");
+    ASSERT_EQ(ToString(*p), "Session 2");
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kInitializerListConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
@@ -449,7 +449,7 @@ TEST(ProxyLifetimeTests, TestPolyAssignment_InPlaceInitializerList_ToNull) {
     pro::proxy<TestFacade> p;
     p.emplace<utils::LifetimeTracker::Session>({ 1, 2, 3 }, &tracker);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 1");
+    ASSERT_EQ(ToString(*p), "Session 1");
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction);
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
@@ -487,9 +487,9 @@ TEST(ProxyLifetimeTests, TestCopyAssignment_FromValue_ToValue) {
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kValueConstruction);
     p1 = p2;
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 4");
+    ASSERT_EQ(ToString(*p1), "Session 4");
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     expected_ops.emplace_back(3, utils::LifetimeOperationType::kCopyConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     expected_ops.emplace_back(4, utils::LifetimeOperationType::kMoveConstruction);
@@ -519,9 +519,9 @@ TEST(ProxyLifetimeTests, TestCopyAssignment_FromValue_ToValue_Exception) {
     }
     ASSERT_TRUE(exception_thrown);
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 1");
+    ASSERT_EQ(ToString(*p1), "Session 1");
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
   expected_ops.emplace_back(2, utils::LifetimeOperationType::kDestruction);
@@ -544,7 +544,7 @@ TEST(ProxyLifetimeTests, TestCopyAssignment_FromValue_ToSelf) {
 #pragma clang diagnostic pop
 #endif  // __clang__
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 3");
+    ASSERT_EQ(ToString(*p), "Session 3");
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     expected_ops.emplace_back(3, utils::LifetimeOperationType::kMoveConstruction);
@@ -564,9 +564,9 @@ TEST(ProxyLifetimeTests, TestCopyAssignment_FromValue_ToNull) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     p1 = p2;
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 3");
+    ASSERT_EQ(ToString(*p1), "Session 3");
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 1");
+    ASSERT_EQ(ToString(*p2), "Session 1");
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction);
     expected_ops.emplace_back(3, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kDestruction);
@@ -595,7 +595,7 @@ TEST(ProxyLifetimeTests, TestCopyAssignment_FromValue_ToNull_Exception) {
     ASSERT_TRUE(exception_thrown);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 1");
+    ASSERT_EQ(ToString(*p2), "Session 1");
     ASSERT_TRUE(tracker.GetOperations() == expected_ops);
   }
   expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
@@ -647,7 +647,7 @@ TEST(ProxyLifetimeTests, TestMoveAssignment_FromValue_ToValue) {
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kValueConstruction);
     p1 = std::move(p2);
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 3");
+    ASSERT_EQ(ToString(*p1), "Session 3");
     ASSERT_FALSE(p2.has_value());
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     expected_ops.emplace_back(3, utils::LifetimeOperationType::kMoveConstruction);
@@ -693,7 +693,7 @@ TEST(ProxyLifetimeTests, TestMoveAssignment_FromValue_ToNull) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     p1 = std::move(p2);
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 2");
+    ASSERT_EQ(ToString(*p1), "Session 2");
     ASSERT_FALSE(p2.has_value());
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
@@ -749,7 +749,7 @@ TEST(ProxyLifetimeTests, TestHasValue) {
   ASSERT_FALSE(p1.has_value());
   p1 = &foo;
   ASSERT_TRUE(p1.has_value());
-  ASSERT_EQ(ToString(p1), "123");
+  ASSERT_EQ(ToString(*p1), "123");
 }
 
 TEST(ProxyLifetimeTests, TestReset_FromValue) {
@@ -782,9 +782,9 @@ TEST(ProxyLifetimeTests, TestSwap_Value_Value) {
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kValueConstruction);
     swap(p1, p2);
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 4");
+    ASSERT_EQ(ToString(*p1), "Session 4");
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 5");
+    ASSERT_EQ(ToString(*p2), "Session 5");
     expected_ops.emplace_back(3, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     expected_ops.emplace_back(4, utils::LifetimeOperationType::kMoveConstruction);
@@ -806,7 +806,7 @@ TEST(ProxyLifetimeTests, TestSwap_Value_Self) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     swap(p, p);
     ASSERT_TRUE(p.has_value());
-    ASSERT_EQ(ToString(p), "Session 3");
+    ASSERT_EQ(ToString(*p), "Session 3");
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
     expected_ops.emplace_back(3, utils::LifetimeOperationType::kMoveConstruction);
@@ -827,7 +827,7 @@ TEST(ProxyLifetimeTests, TestSwap_Value_Null) {
     swap(p1, p2);
     ASSERT_FALSE(p1.has_value());
     ASSERT_TRUE(p2.has_value());
-    ASSERT_EQ(ToString(p2), "Session 2");
+    ASSERT_EQ(ToString(*p2), "Session 2");
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
   }
@@ -844,7 +844,7 @@ TEST(ProxyLifetimeTests, TestSwap_Null_Value) {
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction);
     swap(p1, p2);
     ASSERT_TRUE(p1.has_value());
-    ASSERT_EQ(ToString(p1), "Session 2");
+    ASSERT_EQ(ToString(*p1), "Session 2");
     ASSERT_FALSE(p2.has_value());
     expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction);
     expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
diff --git a/tests/proxy_reflection_tests.cpp b/tests/proxy_reflection_tests.cpp
index 0f4374bd..0f4b917c 100644
--- a/tests/proxy_reflection_tests.cpp
+++ b/tests/proxy_reflection_tests.cpp
@@ -15,9 +15,11 @@ concept ReflectionApplicable = requires(pro::proxy<F> p) {
 
 class RttiReflection {
  public:
-  template <class P>
-  constexpr explicit RttiReflection(std::in_place_type_t<P>)
-      : type_(typeid(P)) {}
+  static constexpr bool is_direct = false;
+
+  template <class T>
+  constexpr explicit RttiReflection(std::in_place_type_t<T>)
+      : type_(typeid(T)) {}
 
   const char* GetName() const noexcept { return type_.name(); }
 
@@ -27,6 +29,8 @@ class RttiReflection {
 
 struct TraitsReflection {
  public:
+  static constexpr bool is_direct = true;
+
   template <class P>
   constexpr explicit TraitsReflection(std::in_place_type_t<P>)
       : is_default_constructible_(std::is_default_constructible_v<P>),
@@ -60,12 +64,12 @@ static_assert(ReflectionApplicable<TestTraitsFacade, TraitsReflection>);
 TEST(ProxyReflectionTests, TestRtti_RawPtr) {
   int foo = 123;
   pro::proxy<TestRttiFacade> p = &foo;
-  ASSERT_EQ(pro::proxy_reflect<RttiReflection>(p).GetName(), typeid(int*).name());
+  ASSERT_STREQ(pro::proxy_reflect<RttiReflection>(p).GetName(), typeid(int).name());
 }
 
 TEST(ProxyReflectionTests, TestRtti_FancyPtr) {
   pro::proxy<TestRttiFacade> p = std::make_unique<double>(1.23);
-  ASSERT_EQ(pro::proxy_reflect<RttiReflection>(p).GetName(), typeid(std::unique_ptr<double>).name());
+  ASSERT_STREQ(pro::proxy_reflect<RttiReflection>(p).GetName(), typeid(double).name());
 }
 
 TEST(ProxyReflectionTests, TestTraits_RawPtr) {
diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp
index a9f21983..39d975da 100644
--- a/tests/proxy_traits_tests.cpp
+++ b/tests/proxy_traits_tests.cpp
@@ -155,6 +155,8 @@ static_assert(std::is_nothrow_assignable_v<pro::proxy<TrivialFacade>, MockFuncti
 static_assert(sizeof(pro::proxy<TrivialFacade>) == 2 * sizeof(void*));  // VTABLE should be eliminated, but a placeholder is required
 
 struct ReflectionOfSmallPtr {
+  static constexpr bool is_direct = true;
+
   template <class P> requires(sizeof(P) <= sizeof(void*))
   constexpr ReflectionOfSmallPtr(std::in_place_type_t<P>) {}
 };
@@ -168,6 +170,8 @@ static_assert(pro::proxiable<MockTrivialPtr, RelocatableFacadeWithReflection>);
 static_assert(pro::proxiable<MockFunctionPtr, RelocatableFacadeWithReflection>);
 
 struct RuntimeReflection {
+  static constexpr bool is_direct = true;
+
   template <class P>
   explicit RuntimeReflection(std::in_place_type_t<P>) { throw std::runtime_error{"Not supported"}; }
 };
@@ -303,9 +307,12 @@ struct BadFacade_MissingReflectionTypes {
 };
 static_assert(!pro::facade<BadFacade_MissingReflectionTypes>);
 
+struct BadReflection {
+  static const bool is_direct;
+};
 struct BadFacade_BadReflectionType {
   using convention_types = std::tuple<>;
-  using reflection_types = std::tuple<std::unique_ptr<int>>;  // Probably constexpr, unknown until the evaluation of proxiablility
+  using reflection_types = std::tuple<BadReflection>;
   static constexpr auto constraints = pro::proxiable_ptr_constraints{
       .max_size = 2 * sizeof(void*),
       .max_align = alignof(void*),
@@ -314,6 +321,7 @@ struct BadFacade_BadReflectionType {
       .destructibility = pro::constraint_level::nothrow,
   };
 };
-static_assert(pro::facade<BadFacade_BadReflectionType>);
+static_assert(!pro::facade<BadFacade_BadReflectionType>);
+const bool BadReflection::is_direct = true;
 
 }  // namespace

From fa1f774bc178e83dec93f434c84ef25eb660ca25 Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Thu, 20 Jun 2024 12:00:56 +0800
Subject: [PATCH 2/5] Strengthen constraints for accessors

---
 proxy.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/proxy.h b/proxy.h
index d9c8df18..052ddd0d 100644
--- a/proxy.h
+++ b/proxy.h
@@ -414,8 +414,9 @@ consteval bool is_meta_is_direct_well_formed() {
 template <class T, class F>
 consteval bool is_a11y_well_formed() {
   if constexpr (requires { typename proxy_accessor<T, F>; }) {
-    return !std::is_final_v<proxy_accessor<T, F>> &&
-        std::is_nothrow_default_constructible_v<proxy_accessor<T, F>>;
+    return std::is_trivial_v<proxy_accessor<T, F>> &&
+        std::is_empty_v<proxy_accessor<T, F>> &&
+        !std::is_final_v<proxy_accessor<T, F>>;
   } else {
     return true;
   }

From 0ab87689ff6c64ef293efbe83c9188c8005352a2 Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Thu, 20 Jun 2024 12:52:02 +0800
Subject: [PATCH 3/5] Fix semantics of facade_traits::has_indirection

---
 proxy.h | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/proxy.h b/proxy.h
index 052ddd0d..0f74193a 100644
--- a/proxy.h
+++ b/proxy.h
@@ -609,8 +609,6 @@ struct facade_conv_traits_impl<F, Cs...> : applicable_traits {
   template <class P>
   static constexpr bool conv_applicable_ptr =
       (conv_traits<Cs>::template applicable_ptr<P> && ...);
-  static constexpr bool conv_has_indirection =
-      (!Cs::dispatch_type::is_direct || ...);
 };
 template <class F, class... Rs>
 struct facade_refl_traits_impl : inapplicable_traits {};
@@ -628,7 +626,6 @@ struct facade_refl_traits_impl<F, Rs...> : applicable_traits {
   template <class P>
   static constexpr bool refl_applicable_ptr =
       (is_reflection_type_well_formed<Rs, P>() && ...);
-  static constexpr bool refl_has_indirection = (!Rs::is_direct || ...);
 };
 template <class F> struct facade_traits : inapplicable_traits {};
 template <class F>
@@ -664,8 +661,8 @@ struct facade_traits<F>
       typename facade_traits::indirect_refl_accessor>;
 
   static constexpr bool applicable = true;
-  static constexpr bool has_indirection = facade_traits::conv_has_indirection ||
-      facade_traits::refl_has_indirection;
+  static constexpr bool has_indirection =
+      !std::is_same_v<indirect_accessor, composite_accessor_impl<>>;
 };
 
 using ptr_prototype = void*[2];

From 238b46be1259aee53ba4126c6dab50294a2fa081 Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Fri, 21 Jun 2024 11:37:50 +0800
Subject: [PATCH 4/5] Fix typo

---
 proxy.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/proxy.h b/proxy.h
index 0f74193a..2926fb6d 100644
--- a/proxy.h
+++ b/proxy.h
@@ -216,8 +216,7 @@ R indirect_conv_dispatcher(const std::byte& self, Args... args)
 }
 template <class P, class D, qualifier_type Q, class R, class... Args>
 R direct_conv_dispatcher(add_qualifier_t<std::byte, Q> self, Args... args)
-    noexcept(invocable_dispatch<
-        D, true, R, typename ptr_traits<P>::target_type&, Args...>) {
+    noexcept(invocable_dispatch<D, true, R, add_qualifier_t<P, Q>, Args...>) {
   using Ptr = add_qualifier_t<P, Q>;
   return invoke_dispatch<D, R>(std::forward<Ptr>(*std::launder(reinterpret_cast<
       std::remove_reference_t<Ptr>*>(&self))), std::forward<Args>(args)...);

From 8a9fb0d5245555c1202ff99a18ceb3c45bdd4e59 Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Fri, 21 Jun 2024 19:05:08 +0800
Subject: [PATCH 5/5] Remove qualifier_of_v which is hard to use correctly

---
 proxy.h | 145 ++++++++++++++++++++++++++++++++------------------------
 1 file changed, 84 insertions(+), 61 deletions(-)

diff --git a/proxy.h b/proxy.h
index 0f74193a..c8802d29 100644
--- a/proxy.h
+++ b/proxy.h
@@ -51,23 +51,6 @@ template <class U, class... T>
 using lazy_eval_t = typename lazy_eval_traits<U, T...>::type;
 
 enum class qualifier_type { lv, const_lv, rv, const_rv };
-
-template <class T> struct qualifier_of_traits;
-template <class T>
-struct qualifier_of_traits<T&>
-    : std::integral_constant<qualifier_type, qualifier_type::lv> {};
-template <class T>
-struct qualifier_of_traits<const T&>
-    : std::integral_constant<qualifier_type, qualifier_type::const_lv> {};
-template <class T>
-struct qualifier_of_traits<T&&>
-    : std::integral_constant<qualifier_type, qualifier_type::rv> {};
-template <class T>
-struct qualifier_of_traits<const T&&>
-    : std::integral_constant<qualifier_type, qualifier_type::const_rv> {};
-template <class T>
-constexpr qualifier_type qualifier_of_v = qualifier_of_traits<T>::value;
-
 template <class T, qualifier_type Q> struct add_qualifier_traits;
 template <class T>
 struct add_qualifier_traits<T, qualifier_type::lv> : std::type_identity<T&> {};
@@ -441,9 +424,9 @@ struct conv_traits_impl<C, Os...> : applicable_traits {
   using meta = composite_meta_impl<dispatcher_meta<
       typename overload_traits<C::dispatch_type::is_direct, Os>
           ::template meta_provider<dispatch_type>>...>;
-  template <class... Args>
-  using matched_overload_traits =
-      std::invoke_result_t<overload_resolver, Args...>;
+  template <qualifier_type Q, class... Args>
+  using matched_overload_traits = std::invoke_result_t<
+      overload_resolver, add_qualifier_t<std::byte, Q>, Args...>;
 
   template <class P>
   static constexpr bool applicable_ptr = (overload_traits<
@@ -601,14 +584,20 @@ struct facade_conv_traits_impl<F, Cs...> : applicable_traits {
       composite_accessor<F, true, typename conv_traits<Cs>::dispatch_type...>;
   using indirect_conv_accessor =
       composite_accessor<F, false, typename conv_traits<Cs>::dispatch_type...>;
-  template <class D, class... Args>
+  template <class D, qualifier_type Q, class... Args>
   using matched_overload_traits = typename conv_traits<first_applicable_t<
       dispatch_match_helper<D>::template traits, Cs...>>
-      ::template matched_overload_traits<Args...>;
+      ::template matched_overload_traits<Q, Args...>;
 
   template <class P>
   static constexpr bool conv_applicable_ptr =
       (conv_traits<Cs>::template applicable_ptr<P> && ...);
+  template <class D, qualifier_type Q, class... Args>
+  static constexpr bool is_invocable =
+      requires { typename matched_overload_traits<D, Q, Args...>; };
+  template <class D, qualifier_type Q, class... Args>
+  static constexpr bool is_nothrow_invocable =
+      matched_overload_traits<D, Q, Args...>::is_noexcept;
 };
 template <class F, class... Rs>
 struct facade_refl_traits_impl : inapplicable_traits {};
@@ -691,28 +680,37 @@ struct meta_ptr<M> : M {
 
 template <class F>
 struct proxy_helper {
-  template <class D, class P, class... Args>
-  using matched_overload_traits = typename facade_traits<F>
-      ::template matched_overload_traits<D, add_qualifier_t<
-          std::byte, qualifier_of_v<P&&>>, Args...>;
-
   static inline const auto& get_meta(const proxy<F>& p) noexcept
       { return *p.meta_.operator->(); }
-  template <class D, class P, class... Args>
-  static decltype(auto) invoke(P&& p, Args&&... args)
-      noexcept(matched_overload_traits<D, P, Args...>::is_noexcept)
-      requires(requires { typename matched_overload_traits<D, P, Args...>; }) {
-    return p.meta_->template dispatcher_meta<typename matched_overload_traits<
-        D, P, Args...>::template meta_provider<D>>::dispatcher(
-        std::forward<add_qualifier_t<std::byte, qualifier_of_v<P&&>>>(*p.ptr_),
+  template <class D, qualifier_type Q, class... Args>
+  static decltype(auto) invoke(add_qualifier_t<proxy<F>, Q> p, Args&&... args) {
+    return p.meta_->template dispatcher_meta<typename facade_traits<F>
+        ::template matched_overload_traits<D, Q, Args...>
+        ::template meta_provider<D>>::dispatcher(
+        std::forward<add_qualifier_t<std::byte, Q>>(*p.ptr_),
         std::forward<Args>(args)...);
   }
-  static inline const proxy<F>& access(
-      const typename facade_traits<F>::indirect_accessor& ia) {
+  template <class A>
+  static const proxy<F>& indirect_access(const A& ia) {
+    using IA = typename facade_traits<F>::indirect_accessor;
+    static_assert(std::is_base_of_v<A, IA>);
     return *reinterpret_cast<const proxy<F>*>(
-        reinterpret_cast<const std::byte*>(&ia) - offsetof(proxy<F>, ia_));
+        reinterpret_cast<const std::byte*>(static_cast<const IA*>(&ia)) -
+        offsetof(proxy<F>, ia_));
   }
 };
+template <class P> struct access_proxy_traits;
+template <class F>
+struct access_proxy_traits<proxy<F>> { using helper = proxy_helper<F>; };
+template <class P, qualifier_type Q, class A>
+decltype(auto) access_proxy_impl(add_qualifier_t<A, Q> a) {
+  if constexpr (std::is_base_of_v<A, P>) {
+    return static_cast<add_qualifier_t<P, Q>>(
+        std::forward<add_qualifier_t<A, Q>>(a));
+  } else {
+    return access_proxy_traits<P>::helper::template indirect_access(a);
+  }
+}
 
 }  // namespace details
 
@@ -778,8 +776,6 @@ class proxy : public details::facade_traits<F>::direct_accessor {
   static constexpr bool HasMoveAssignment = HasMoveConstructor && HasDestructor;
 
  public:
-  using facade_type = F;
-
   proxy() noexcept = default;
   proxy(std::nullptr_t) noexcept : proxy() {}
   proxy(const proxy& rhs) noexcept(HasNothrowCopyConstructor)
@@ -936,40 +932,67 @@ class proxy : public details::facade_traits<F>::direct_accessor {
 
 template <class D, facade F, class... Args>
 decltype(auto) proxy_invoke(proxy<F>& p, Args&&... args)
-    ___PRO_DIRECT_FUNC_IMPL(details::proxy_helper<F>::template invoke<D>(
-        p, std::forward<Args>(args)...))
+    noexcept(details::facade_traits<F>::template is_nothrow_invocable<
+        D, details::qualifier_type::lv, Args...>)
+    requires(details::facade_traits<F>::template is_invocable<
+        D, details::qualifier_type::lv, Args...>) {
+  return details::proxy_helper<F>::template invoke<
+      D, details::qualifier_type::lv>(p, std::forward<Args>(args)...);
+}
 template <class D, facade F, class... Args>
 decltype(auto) proxy_invoke(const proxy<F>& p, Args&&... args)
-    ___PRO_DIRECT_FUNC_IMPL(details::proxy_helper<F>::template invoke<D>(
-        p, std::forward<Args>(args)...))
+    noexcept(details::facade_traits<F>::template is_nothrow_invocable<
+        D, details::qualifier_type::const_lv, Args...>)
+    requires(details::facade_traits<F>::template is_invocable<
+        D, details::qualifier_type::const_lv, Args...>) {
+  return details::proxy_helper<F>::template invoke<
+      D, details::qualifier_type::const_lv>(p, std::forward<Args>(args)...);
+}
 template <class D, facade F, class... Args>
 decltype(auto) proxy_invoke(proxy<F>&& p, Args&&... args)
-    ___PRO_DIRECT_FUNC_IMPL(details::proxy_helper<F>::template invoke<D>(
-        static_cast<proxy<F>&&>(p), std::forward<Args>(args)...))
+    noexcept(details::facade_traits<F>::template is_nothrow_invocable<
+        D, details::qualifier_type::rv, Args...>)
+    requires(details::facade_traits<F>::template is_invocable<
+        D, details::qualifier_type::rv, Args...>) {
+  return details::proxy_helper<F>::template invoke<
+      D, details::qualifier_type::rv>(
+      std::forward<proxy<F>>(p), std::forward<Args>(args)...);
+}
 template <class D, facade F, class... Args>
 decltype(auto) proxy_invoke(const proxy<F>&& p, Args&&... args)
-    ___PRO_DIRECT_FUNC_IMPL(details::proxy_helper<F>::template invoke<D>(
-        static_cast<const proxy<F>&&>(p), std::forward<Args>(args)...))
+    noexcept(details::facade_traits<F>::template is_nothrow_invocable<
+        D, details::qualifier_type::const_rv, Args...>)
+    requires(details::facade_traits<F>::template is_invocable<
+        D, details::qualifier_type::const_rv, Args...>) {
+  return details::proxy_helper<F>::template invoke<
+      D, details::qualifier_type::const_rv>(
+      std::forward<const proxy<F>>(p), std::forward<Args>(args)...);
+}
+
+template <class P, class A>
+decltype(auto) access_proxy(A& a) noexcept {
+  return details::access_proxy_impl<P, details::qualifier_type::lv, A>(a);
+}
+template <class P, class A>
+decltype(auto) access_proxy(const A& a) noexcept {
+  return details::access_proxy_impl<P, details::qualifier_type::const_lv, A>(a);
+}
+template <class P, class A>
+decltype(auto) access_proxy(A&& a) noexcept {
+  return details::access_proxy_impl<P, details::qualifier_type::rv, A>(
+      std::forward<A>(a));
+}
+template <class P, class A>
+decltype(auto) access_proxy(const A&& a) noexcept {
+  return details::access_proxy_impl<P, details::qualifier_type::const_rv, A>(
+      std::forward<const A>(a));
+}
 
 template <class R, facade F>
 const R& proxy_reflect(const proxy<F>& p) noexcept
     requires(details::facade_traits<F>::template has_refl<R>)
     { return details::proxy_helper<F>::get_meta(p); }
 
-template <class P, class A>
-decltype(auto) access_proxy(A&& a) noexcept {
-  if constexpr (std::is_base_of_v<std::remove_cvref_t<A>, P>) {
-    return static_cast<details::add_qualifier_t<
-        P, details::qualifier_of_v<A&&>>>(std::forward<A>(a));
-  } else {
-    using IA = typename details::facade_traits<typename P::facade_type>
-        ::indirect_accessor;
-    static_assert(std::is_base_of_v<std::remove_cvref_t<A>, IA>);
-    return details::proxy_helper<typename P::facade_type>
-        ::access(static_cast<const IA&>(a));
-  }
-}
-
 namespace details {
 
 template <class T>
