Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.

Commit 4f8eda6

Browse files
author
Mingxin Wang
authored
Align behavior of rvalue reference (#271)
1 parent 4f6d5f9 commit 4f8eda6

4 files changed

Lines changed: 42 additions & 52 deletions

File tree

proxy.h

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -198,25 +198,24 @@ class destruction_guard {
198198
T* p_;
199199
};
200200

201-
template <class P, qualifier_type Q, bool NE>
202-
struct ptr_traits : inapplicable_traits {};
203-
template <class P, qualifier_type Q, bool NE>
204-
requires(requires { *std::declval<add_qualifier_t<P, Q>>(); } &&
205-
(!NE || noexcept(*std::declval<add_qualifier_t<P, Q>>())))
206-
struct ptr_traits<P, Q, NE> : applicable_traits
207-
{ using target_type = decltype(*std::declval<add_qualifier_t<P, Q>>()); };
201+
template <bool IsDirect, class P, qualifier_type Q>
202+
struct operand_traits : add_qualifier_traits<P, Q> {};
203+
template <class P, qualifier_type Q>
204+
struct operand_traits<false, P, Q>
205+
: std::type_identity<decltype(*std::declval<add_qualifier_t<P, Q>>())> {};
206+
template <bool IsDirect, class P, qualifier_type Q>
207+
using operand_t = typename operand_traits<IsDirect, P, Q>::type;
208208

209209
template <class D, bool NE, class R, class... Args>
210210
concept invocable_dispatch = (NE && std::is_nothrow_invocable_r_v<
211211
R, D, Args...>) || (!NE && std::is_invocable_r_v<R, D, Args...>);
212-
template <class D, class P, qualifier_type Q, bool NE, class R, class... Args>
213-
concept invocable_dispatch_ptr_indirect = ptr_traits<P, Q, NE>::applicable &&
214-
invocable_dispatch<
215-
D, NE, R, typename ptr_traits<P, Q, NE>::target_type, Args...>;
216-
template <class D, class P, qualifier_type Q, bool NE, class R, class... Args>
217-
concept invocable_dispatch_ptr_direct = invocable_dispatch<
218-
D, NE, R, add_qualifier_t<P, Q>, Args...> && (Q != qualifier_type::rv ||
219-
(NE && std::is_nothrow_destructible_v<P>) ||
212+
template <bool IsDirect, class D, class P, qualifier_type Q, bool NE, class R,
213+
class... Args>
214+
concept invocable_dispatch_ptr =
215+
(IsDirect || (requires { *std::declval<add_qualifier_t<P, Q>>(); } &&
216+
(!NE || noexcept(*std::declval<add_qualifier_t<P, Q>>())))) &&
217+
invocable_dispatch<D, NE, R, operand_t<IsDirect, P, Q>, Args...> &&
218+
(Q != qualifier_type::rv || (NE && std::is_nothrow_destructible_v<P>) ||
220219
(!NE && std::is_destructible_v<P>));
221220

222221
template <class D, class R, class... Args>
@@ -227,25 +226,27 @@ R invoke_dispatch(Args&&... args) {
227226
return D{}(std::forward<Args>(args)...);
228227
}
229228
}
230-
template <class D, class P, qualifier_type Q, class R, class... Args>
231-
R indirect_conv_dispatcher(add_qualifier_t<std::byte, Q> self, Args... args)
232-
noexcept(invocable_dispatch_ptr_indirect<D, P, Q, true, R, Args...>) {
233-
auto& qp = *std::launder(reinterpret_cast<add_qualifier_ptr_t<P, Q>>(&self));
234-
if constexpr (std::is_constructible_v<bool, decltype(qp)>) { assert(qp); }
235-
return invoke_dispatch<D, R>(*std::forward<add_qualifier_t<P, Q>>(qp),
236-
std::forward<Args>(args)...);
229+
template <bool IsDirect, class P, qualifier_type Q, class T>
230+
decltype(auto) get_operand(T& ptr) {
231+
if constexpr (IsDirect) {
232+
return std::forward<add_qualifier_t<P, Q>>(ptr);
233+
} else {
234+
if constexpr (std::is_constructible_v<bool, T&>) { assert(ptr); }
235+
return *std::forward<add_qualifier_t<P, Q>>(ptr);
236+
}
237237
}
238-
template <class D, class P, qualifier_type Q, class R, class... Args>
239-
R direct_conv_dispatcher(add_qualifier_t<std::byte, Q> self, Args... args)
240-
noexcept(invocable_dispatch_ptr_direct<D, P, Q, true, R, Args...>) {
238+
template <bool IsDirect, class D, class P, qualifier_type Q, class R,
239+
class... Args>
240+
R conv_dispatcher(add_qualifier_t<std::byte, Q> self, Args... args)
241+
noexcept(invocable_dispatch_ptr<IsDirect, D, P, Q, true, R, Args...>) {
241242
auto& qp = *std::launder(reinterpret_cast<add_qualifier_ptr_t<P, Q>>(&self));
242243
if constexpr (Q == qualifier_type::rv) {
243244
destruction_guard guard{&qp};
244-
return invoke_dispatch<D, R>(
245-
std::forward<add_qualifier_t<P, Q>>(qp), std::forward<Args>(args)...);
245+
return invoke_dispatch<D, R>(get_operand<IsDirect, P, Q>(qp),
246+
std::forward<Args>(args)...);
246247
} else {
247-
return invoke_dispatch<D, R>(
248-
std::forward<add_qualifier_t<P, Q>>(qp), std::forward<Args>(args)...);
248+
return invoke_dispatch<D, R>(get_operand<IsDirect, P, Q>(qp),
249+
std::forward<Args>(args)...);
249250
}
250251
}
251252
template <class D, qualifier_type Q, class R, class... Args>
@@ -263,28 +264,16 @@ struct overload_traits_impl : applicable_traits {
263264

264265
template <bool IsDirect, class D, class P>
265266
static consteval bool is_applicable_ptr() {
266-
if constexpr (IsDirect) {
267-
if constexpr (invocable_dispatch_ptr_direct<D, P, Q, NE, R, Args...>) {
268-
return true;
269-
} else {
270-
return invocable_dispatch<D, NE, R, std::nullptr_t, Args...>;
271-
}
267+
if constexpr (invocable_dispatch_ptr<IsDirect, D, P, Q, NE, R, Args...>) {
268+
return true;
272269
} else {
273-
if constexpr (invocable_dispatch_ptr_indirect<D, P, Q, NE, R, Args...>) {
274-
return true;
275-
} else {
276-
return invocable_dispatch<D, NE, R, std::nullptr_t, Args...>;
277-
}
270+
return invocable_dispatch<D, NE, R, std::nullptr_t, Args...>;
278271
}
279272
}
280273
template <bool IsDirect, class D, class P>
281274
static consteval dispatcher_type get_dispatcher() {
282-
if constexpr (!IsDirect &&
283-
invocable_dispatch_ptr_indirect<D, P, Q, NE, R, Args...>) {
284-
return &indirect_conv_dispatcher<D, P, Q, R, Args...>;
285-
} else if constexpr (IsDirect &&
286-
invocable_dispatch_ptr_direct<D, P, Q, NE, R, Args...>) {
287-
return &direct_conv_dispatcher<D, P, Q, R, Args...>;
275+
if constexpr (invocable_dispatch_ptr<IsDirect, D, P, Q, NE, R, Args...>) {
276+
return &conv_dispatcher<IsDirect, D, P, Q, R, Args...>;
288277
} else {
289278
return &default_conv_dispatcher<D, Q, R, Args...>;
290279
}
@@ -791,8 +780,7 @@ struct proxy_helper {
791780
static decltype(auto) invoke(add_qualifier_t<proxy<F>, Q> p, Args&&... args) {
792781
auto dispatcher = get_meta(p).template invocation_meta<IsDirect, D, O>
793782
::dispatcher;
794-
if constexpr (
795-
IsDirect && overload_traits<O>::qualifier == qualifier_type::rv) {
783+
if constexpr (overload_traits<O>::qualifier == qualifier_type::rv) {
796784
meta_ptr_reset_guard guard{p.meta_};
797785
return dispatcher(std::forward<add_qualifier_t<std::byte, Q>>(*p.ptr_),
798786
std::forward<Args>(args)...);
@@ -1327,8 +1315,8 @@ class strong_compact_ptr
13271315
{ return std::launder(reinterpret_cast<const T*>(&this->ptr_->value)); }
13281316
T& operator*() & noexcept { return *operator->(); }
13291317
const T& operator*() const& noexcept { return *operator->(); }
1330-
T&& operator*() && noexcept { return std::move(operator->()); }
1331-
const T&& operator*() const&& noexcept { return std::move(operator->()); }
1318+
T&& operator*() && noexcept { return std::move(*operator->()); }
1319+
const T&& operator*() const&& noexcept { return std::move(*operator->()); }
13321320

13331321
private:
13341322
explicit strong_compact_ptr(Storage* ptr) noexcept

tests/proxy_creation_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,5 +994,6 @@ TEST(ProxyCreationTests, TestMakeProxyView) {
994994
ASSERT_EQ((*p)(), 0);
995995
ASSERT_EQ((*std::as_const(p))(), 1);
996996
ASSERT_EQ((*std::move(p))(), 2);
997+
p = pro::make_proxy_view<TestFacade>(test_callable);
997998
ASSERT_EQ((*std::move(std::as_const(p)))(), 3);
998999
}

tests/proxy_invocation_tests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ TEST(ProxyInvocationTests, TestQualifiedConvention_Member) {
333333
ASSERT_EQ((*p)(), 0);
334334
ASSERT_EQ((*std::as_const(p))(), 1);
335335
ASSERT_EQ((*std::move(p))(), 2);
336+
p = pro::make_proxy<TestFacade, TestCallable>();
336337
ASSERT_EQ((*std::move(std::as_const(p)))(), 3);
337338
}
338339

@@ -347,5 +348,6 @@ TEST(ProxyInvocationTests, TestQualifiedConvention_Free) {
347348
ASSERT_EQ(Dump(*p), "is_const=false, is_ref=true, value=123");
348349
ASSERT_EQ(Dump(*std::as_const(p)), "is_const=true, is_ref=true, value=123");
349350
ASSERT_EQ(Dump(*std::move(p)), "is_const=false, is_ref=false, value=123");
351+
p = pro::make_proxy<TestFacade>(123);
350352
ASSERT_EQ(Dump(*std::move(std::as_const(p))), "is_const=true, is_ref=false, value=123");
351353
}

tests/proxy_rtti_tests.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ TEST(ProxyRttiTests, TestIndirectCast_Move_Succeed) {
9191
auto p = pro::make_proxy<details::TestFacade>(v1);
9292
auto v2 = proxy_cast<std::vector<int>>(std::move(*p));
9393
ASSERT_EQ(v2, v1);
94-
v2 = proxy_cast<std::vector<int>>(std::move(*p));
95-
ASSERT_TRUE(v2.empty());
94+
ASSERT_FALSE(p.has_value());
9695
}
9796

9897
TEST(ProxyRttiTests, TestIndirectCast_Move_Fail) {

0 commit comments

Comments
 (0)